summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.moxie2
-rw-r--r--releases.moxie20
-rw-r--r--src/main/distrib/data/defaults.properties15
-rw-r--r--src/main/distrib/data/groovy/sendmail-html.groovy2
-rw-r--r--src/main/java/com/gitblit/ConfigUserService.java2
-rw-r--r--src/main/java/com/gitblit/Constants.java37
-rw-r--r--src/main/java/com/gitblit/GitBlitServer.java3
-rw-r--r--src/main/java/com/gitblit/auth/AuthenticationProvider.java6
-rw-r--r--src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/auth/LdapAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/auth/PAMAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/auth/RedmineAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/auth/SalesforceAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/auth/WindowsAuthProvider.java2
-rw-r--r--src/main/java/com/gitblit/client/EditUserDialog.java2
-rw-r--r--src/main/java/com/gitblit/git/PatchsetReceivePack.java3
-rw-r--r--src/main/java/com/gitblit/manager/AuthenticationManager.java14
-rw-r--r--src/main/java/com/gitblit/manager/RepositoryManager.java71
-rw-r--r--src/main/java/com/gitblit/models/RepositoryModel.java3
-rw-r--r--src/main/java/com/gitblit/models/TicketModel.java6
-rw-r--r--src/main/java/com/gitblit/models/UserModel.java7
-rw-r--r--src/main/java/com/gitblit/service/MailService.java17
-rw-r--r--src/main/java/com/gitblit/tickets/TicketNotifier.java4
-rw-r--r--src/main/java/com/gitblit/utils/ArrayUtils.java2
-rw-r--r--src/main/java/com/gitblit/utils/CommitCache.java117
-rw-r--r--src/main/java/com/gitblit/utils/JGitUtils.java343
-rw-r--r--src/main/java/com/gitblit/utils/MarkdownUtils.java5
-rw-r--r--src/main/java/com/gitblit/utils/SecureRandom.java83
-rw-r--r--src/main/java/com/gitblit/wicket/GitBlitWebApp.properties2
-rw-r--r--src/main/java/com/gitblit/wicket/GitBlitWebApp_nl.properties2
-rw-r--r--src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_TW.properties1437
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BasePage.html1
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html3
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java6
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditUserPage.java2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TicketPage.java6
-rw-r--r--src/main/java/com/gitblit/wicket/panels/PagerPanel.java5
-rw-r--r--src/main/java/welcome_zh_TW.mkd2
-rw-r--r--src/main/resources/bootstrap-fixes.css25
-rw-r--r--src/test/java/com/gitblit/tests/MarkdownUtilsTest.java74
-rw-r--r--src/test/java/com/gitblit/utils/SecureRandomTest.java33
41 files changed, 1485 insertions, 889 deletions
diff --git a/build.moxie b/build.moxie
index e1dbdd57..e84ab401 100644
--- a/build.moxie
+++ b/build.moxie
@@ -10,7 +10,7 @@ name: Gitblit
description: pure Java Git solution
groupId: com.gitblit
artifactId: gitblit
-version: 1.8.0
+version: 1.8.1-SNAPSHOT
inceptionYear: 2011
# Current stable release
diff --git a/releases.moxie b/releases.moxie
index 554028a4..be1200eb 100644
--- a/releases.moxie
+++ b/releases.moxie
@@ -1,4 +1,22 @@
#
+# ${project.version} release
+#
+r30: {
+ title: ${project.name} ${project.version} released
+ id: ${project.version}
+ date: ${project.buildDate}
+ note: ~
+ html: ~
+ text: ~
+ security: ~
+ fixes: ~
+ changes: ~
+ additions: ~
+ dependencyChanges: ~
+ contributors: ~
+}
+
+#
# 1.8.0 release
#
r29: {
@@ -1832,6 +1850,6 @@ r1: {
- James Moger
}
-snapshot: ~
+snapshot: &r30
release: &r29
releases: &r[1..29]
diff --git a/src/main/distrib/data/defaults.properties b/src/main/distrib/data/defaults.properties
index b9d77fe7..4b12ba24 100644
--- a/src/main/distrib/data/defaults.properties
+++ b/src/main/distrib/data/defaults.properties
@@ -567,6 +567,21 @@ tickets.acceptNewPatchsets = true
# SINCE 1.4.0
tickets.requireApproval = false
+# Default setting to control how patchsets are merged to the integration branch.
+# Valid values:
+# MERGE_ALWAYS - Always merge with a merge commit. Every ticket will show up as a branch,
+# even if it could have been fast-forward merged. This is the default.
+# MERGE_IF_NECESSARY - If possible, fast-forward the integration branch,
+# if not, merge with a merge commit.
+# FAST_FORWARD_ONLY - Only merge when a fast-forward is possible. This produces a strictly
+# linear history of the integration branch.
+#
+# This setting can be overriden per-repository.
+#
+# RESTART REQUIRED
+# SINCE 1.9.0
+tickets.mergeType = MERGE_ALWAYS
+
# The case-insensitive regular expression used to identify and close tickets on
# push to the integration branch for commits that are NOT already referenced as
# a patchset tip.
diff --git a/src/main/distrib/data/groovy/sendmail-html.groovy b/src/main/distrib/data/groovy/sendmail-html.groovy
index 26925568..305c6404 100644
--- a/src/main/distrib/data/groovy/sendmail-html.groovy
+++ b/src/main/distrib/data/groovy/sendmail-html.groovy
@@ -336,7 +336,7 @@ class HtmlMailWriter {
}
builder.td() {
mkp.yield header.oldPath
- mkp.yieldUnescaped "<b> -&rt; </b>"
+ mkp.yieldUnescaped "<b> -&gt; </b>"
a(href:blobDiffUrl(id, header.newPath), header.newPath)
}
}
diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java
index 6d7230f7..025b1d8c 100644
--- a/src/main/java/com/gitblit/ConfigUserService.java
+++ b/src/main/java/com/gitblit/ConfigUserService.java
@@ -898,7 +898,7 @@ public class ConfigUserService implements IUserService {
user.countryCode = config.getString(USER, username, COUNTRYCODE);
user.cookie = config.getString(USER, username, COOKIE);
if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) {
- user.cookie = StringUtils.getSHA1(user.username + user.password);
+ user.cookie = user.createCookie();
}
// preferences
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index 6232552e..ab503bd3 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -62,6 +62,12 @@ public class Constants {
public static final String GIT_PATH = "/git/";
public static final String REGEX_SHA256 = "[a-fA-F0-9]{64}";
+
+ /**
+ * This regular expression is used when searching for "mentions" in tickets
+ * (when someone writes @thisOtherUser)
+ */
+ public static final String REGEX_TICKET_MENTION = "\\B@(?<user>[^\\s]+)\\b";
public static final String ZIP_PATH = "/zip/";
@@ -639,6 +645,37 @@ public class Constants {
}
}
+ /**
+ * The type of merge Gitblit will use when merging a ticket to the integration branch.
+ * <p>
+ * The default type is MERGE_ALWAYS.
+ * <p>
+ * This is modeled after the Gerrit SubmitType.
+ */
+ public static enum MergeType {
+ /** Allows a merge only if it can be fast-forward merged into the integration branch. */
+ FAST_FORWARD_ONLY,
+ /** Uses a fast-forward merge if possible, other wise a merge commit is created. */
+ MERGE_IF_NECESSARY,
+ // Future REBASE_IF_NECESSARY,
+ /** Always merge with a merge commit, even when a fast-forward would be possible. */
+ MERGE_ALWAYS,
+ // Future? CHERRY_PICK
+ ;
+
+ public static final MergeType DEFAULT_MERGE_TYPE = MERGE_ALWAYS;
+
+ public static MergeType fromName(String name) {
+ for (MergeType type : values()) {
+ if (type.name().equalsIgnoreCase(name)) {
+ return type;
+ }
+ }
+ return DEFAULT_MERGE_TYPE;
+ }
+ }
+
+
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Unused {
diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java
index d56d9c0c..6123a872 100644
--- a/src/main/java/com/gitblit/GitBlitServer.java
+++ b/src/main/java/com/gitblit/GitBlitServer.java
@@ -375,7 +375,8 @@ public class GitBlitServer {
HashSessionManager sessionManager = new HashSessionManager();
sessionManager.setHttpOnly(true);
// Use secure cookies if only serving https
- sessionManager.setSecureRequestOnly(params.port <= 0 && params.securePort > 0);
+ sessionManager.setSecureRequestOnly( (params.port <= 0 && params.securePort > 0) ||
+ (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) );
rootContext.getSessionHandler().setSessionManager(sessionManager);
// Ensure there is a defined User Service
diff --git a/src/main/java/com/gitblit/auth/AuthenticationProvider.java b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
index 0bfe2351..e359fd7e 100644
--- a/src/main/java/com/gitblit/auth/AuthenticationProvider.java
+++ b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
@@ -78,10 +78,10 @@ public abstract class AuthenticationProvider {
public abstract AuthenticationType getAuthenticationType();
- protected void setCookie(UserModel user, char [] password) {
+ protected void setCookie(UserModel user) {
// create a user cookie
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
- user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ if (StringUtils.isEmpty(user.cookie)) {
+ user.cookie = user.createCookie();
}
}
diff --git a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
index 2cdabf6f..3a6cb8ec 100644
--- a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
@@ -196,7 +196,7 @@ public class HtpasswdAuthProvider extends UsernamePasswordAuthenticationProvider
}
// create a user cookie
- setCookie(user, password);
+ setCookie(user);
// Set user attributes, hide password from backing user service.
user.password = Constants.EXTERNAL_ACCOUNT;
diff --git a/src/main/java/com/gitblit/auth/LdapAuthProvider.java b/src/main/java/com/gitblit/auth/LdapAuthProvider.java
index 7ea8f113..6a2cbde2 100644
--- a/src/main/java/com/gitblit/auth/LdapAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/LdapAuthProvider.java
@@ -292,7 +292,7 @@ public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider {
}
// create a user cookie
- setCookie(user, password);
+ setCookie(user);
if (!supportsTeamMembershipChanges()) {
getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
diff --git a/src/main/java/com/gitblit/auth/PAMAuthProvider.java b/src/main/java/com/gitblit/auth/PAMAuthProvider.java
index 46f4dd6a..b38d49df 100644
--- a/src/main/java/com/gitblit/auth/PAMAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/PAMAuthProvider.java
@@ -122,7 +122,7 @@ public class PAMAuthProvider extends UsernamePasswordAuthenticationProvider {
}
// create a user cookie
- setCookie(user, password);
+ setCookie(user);
// update user attributes from UnixUser
user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
index 27cece29..364aff04 100644
--- a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
@@ -139,7 +139,7 @@ public class RedmineAuthProvider extends UsernamePasswordAuthenticationProvider
}
// create a user cookie
- setCookie(user, password);
+ setCookie(user);
// update user attributes from Redmine
user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
index df033c27..79c3a0c4 100644
--- a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
@@ -66,7 +66,7 @@ public class SalesforceAuthProvider extends UsernamePasswordAuthenticationProvid
user = new UserModel(simpleUsername);
}
- setCookie(user, password);
+ setCookie(user);
setUserAttributes(user, info);
updateUser(user);
diff --git a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
index aee51008..4c31fb15 100644
--- a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
@@ -153,7 +153,7 @@ public class WindowsAuthProvider extends UsernamePasswordAuthenticationProvider
}
// create a user cookie
- setCookie(user, password);
+ setCookie(user);
// update user attributes from Windows identity
user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/client/EditUserDialog.java b/src/main/java/com/gitblit/client/EditUserDialog.java
index 676916b2..4b01ff04 100644
--- a/src/main/java/com/gitblit/client/EditUserDialog.java
+++ b/src/main/java/com/gitblit/client/EditUserDialog.java
@@ -330,7 +330,7 @@ public class EditUserDialog extends JDialog {
}
// change the cookie
- user.cookie = StringUtils.getSHA1(user.username + password);
+ user.cookie = user.createCookie();
String type = settings.get(Keys.realm.passwordStorage).getString("md5");
if (type.equalsIgnoreCase("md5")) {
diff --git a/src/main/java/com/gitblit/git/PatchsetReceivePack.java b/src/main/java/com/gitblit/git/PatchsetReceivePack.java
index 33fa4705..4a09139a 100644
--- a/src/main/java/com/gitblit/git/PatchsetReceivePack.java
+++ b/src/main/java/com/gitblit/git/PatchsetReceivePack.java
@@ -599,7 +599,7 @@ public class PatchsetReceivePack extends GitblitReceivePack {
}
// ensure that the patchset can be cleanly merged right now
- MergeStatus status = JGitUtils.canMerge(getRepository(), tipCommit.getName(), forBranch);
+ MergeStatus status = JGitUtils.canMerge(getRepository(), tipCommit.getName(), forBranch, repository.mergeType);
switch (status) {
case ALREADY_MERGED:
sendError("");
@@ -1279,6 +1279,7 @@ public class PatchsetReceivePack extends GitblitReceivePack {
getRepository(),
patchset.tip,
ticket.mergeTo,
+ getRepositoryModel().mergeType,
committer,
message);
diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java
index 49787631..0a4d8ed7 100644
--- a/src/main/java/com/gitblit/manager/AuthenticationManager.java
+++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java
@@ -608,6 +608,11 @@ public class AuthenticationManager implements IAuthenticationManager {
userCookie = new Cookie(Constants.NAME, cookie);
// expire the cookie in 7 days
userCookie.setMaxAge((int) TimeUnit.DAYS.toSeconds(7));
+
+ // Set cookies HttpOnly so they are not accessible to JavaScript engines
+ userCookie.setHttpOnly(true);
+ // Set secure cookie if only HTTPS is used
+ userCookie.setSecure(httpsOnly());
}
}
String path = "/";
@@ -622,6 +627,15 @@ public class AuthenticationManager implements IAuthenticationManager {
}
}
+
+ private boolean httpsOnly() {
+ int port = settings.getInteger(Keys.server.httpPort, 0);
+ int tlsPort = settings.getInteger(Keys.server.httpsPort, 0);
+ return (port <= 0 && tlsPort > 0) ||
+ (port > 0 && tlsPort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true) );
+ }
+
+
/**
* Logout a user.
*
diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java
index e9bf5b84..2be65873 100644
--- a/src/main/java/com/gitblit/manager/RepositoryManager.java
+++ b/src/main/java/com/gitblit/manager/RepositoryManager.java
@@ -63,6 +63,7 @@ import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.CommitMessageRenderer;
import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.Constants.MergeType;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.GitBlitException;
@@ -899,6 +900,7 @@ public class RepositoryManager implements IRepositoryManager {
model.acceptNewTickets = getConfig(config, "acceptNewTickets", true);
model.requireApproval = getConfig(config, "requireApproval", settings.getBoolean(Keys.tickets.requireApproval, false));
model.mergeTo = getConfig(config, "mergeTo", null);
+ model.mergeType = MergeType.fromName(getConfig(config, "mergeType", settings.getString(Keys.tickets.mergeType, null)));
model.useIncrementalPushTags = getConfig(config, "useIncrementalPushTags", false);
model.incrementalPushTagPrefix = getConfig(config, "incrementalPushTagPrefix", null);
model.allowForks = getConfig(config, "allowForks", true);
@@ -1557,6 +1559,13 @@ public class RepositoryManager implements IRepositoryManager {
if (!StringUtils.isEmpty(repository.mergeTo)) {
config.setString(Constants.CONFIG_GITBLIT, null, "mergeTo", repository.mergeTo);
}
+ if (repository.mergeType == null || repository.mergeType == MergeType.fromName(settings.getString(Keys.tickets.mergeType, null))) {
+ // use default
+ config.unset(Constants.CONFIG_GITBLIT, null, "mergeType");
+ } else {
+ // override default
+ config.setString(Constants.CONFIG_GITBLIT, null, "mergeType", repository.mergeType.name());
+ }
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useIncrementalPushTags", repository.useIncrementalPushTags);
if (StringUtils.isEmpty(repository.incrementalPushTagPrefix) ||
repository.incrementalPushTagPrefix.equals(settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"))) {
@@ -1952,39 +1961,47 @@ public class RepositoryManager implements IRepositoryManager {
}
protected void configureCommitCache() {
- int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14);
+ final int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14);
if (daysToCache <= 0) {
logger.info("Commit cache is disabled");
- } else {
- long start = System.nanoTime();
- long repoCount = 0;
- long commitCount = 0;
- logger.info(MessageFormat.format("Preparing {0} day commit cache. please wait...", daysToCache));
- CommitCache.instance().setCacheDays(daysToCache);
- Date cutoff = CommitCache.instance().getCutoffDate();
- for (String repositoryName : getRepositoryList()) {
- RepositoryModel model = getRepositoryModel(repositoryName);
- if (model != null && model.hasCommits && model.lastChange.after(cutoff)) {
- repoCount++;
- Repository repository = getRepository(repositoryName);
- for (RefModel ref : JGitUtils.getLocalBranches(repository, true, -1)) {
- if (!ref.getDate().after(cutoff)) {
- // branch not recently updated
- continue;
- }
- List<?> commits = CommitCache.instance().getCommits(repositoryName, repository, ref.getName());
- if (commits.size() > 0) {
- logger.info(MessageFormat.format(" cached {0} commits for {1}:{2}",
- commits.size(), repositoryName, ref.getName()));
- commitCount += commits.size();
+ return;
+ }
+ logger.info(MessageFormat.format("Preparing {0} day commit cache...", daysToCache));
+ CommitCache.instance().setCacheDays(daysToCache);
+ Thread loader = new Thread() {
+ @Override
+ public void run() {
+ long start = System.nanoTime();
+ long repoCount = 0;
+ long commitCount = 0;
+ Date cutoff = CommitCache.instance().getCutoffDate();
+ for (String repositoryName : getRepositoryList()) {
+ RepositoryModel model = getRepositoryModel(repositoryName);
+ if (model != null && model.hasCommits && model.lastChange.after(cutoff)) {
+ repoCount++;
+ Repository repository = getRepository(repositoryName);
+ for (RefModel ref : JGitUtils.getLocalBranches(repository, true, -1)) {
+ if (!ref.getDate().after(cutoff)) {
+ // branch not recently updated
+ continue;
+ }
+ List<?> commits = CommitCache.instance().getCommits(repositoryName, repository, ref.getName());
+ if (commits.size() > 0) {
+ logger.info(MessageFormat.format(" cached {0} commits for {1}:{2}",
+ commits.size(), repositoryName, ref.getName()));
+ commitCount += commits.size();
+ }
}
+ repository.close();
}
- repository.close();
}
+ logger.info(MessageFormat.format("built {0} day commit cache of {1} commits across {2} repositories in {3} msecs",
+ daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
}
- logger.info(MessageFormat.format("built {0} day commit cache of {1} commits across {2} repositories in {3} msecs",
- daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
- }
+ };
+ loader.setName("CommitCacheLoader");
+ loader.setDaemon(true);
+ loader.start();
}
protected void confirmWriteAccess() {
diff --git a/src/main/java/com/gitblit/models/RepositoryModel.java b/src/main/java/com/gitblit/models/RepositoryModel.java
index a81c622a..67ee1c7e 100644
--- a/src/main/java/com/gitblit/models/RepositoryModel.java
+++ b/src/main/java/com/gitblit/models/RepositoryModel.java
@@ -28,6 +28,7 @@ import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.CommitMessageRenderer;
import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.Constants.MergeType;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ModelUtils;
import com.gitblit.utils.StringUtils;
@@ -89,6 +90,7 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
public boolean acceptNewTickets;
public boolean requireApproval;
public String mergeTo;
+ public MergeType mergeType;
public transient boolean isCollectingGarbage;
public Date lastGC;
@@ -111,6 +113,7 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
this.isBare = true;
this.acceptNewTickets = true;
this.acceptNewPatchsets = true;
+ this.mergeType = MergeType.DEFAULT_MERGE_TYPE;
addOwner(owner);
}
diff --git a/src/main/java/com/gitblit/models/TicketModel.java b/src/main/java/com/gitblit/models/TicketModel.java
index d5345891..65e29dc0 100644
--- a/src/main/java/com/gitblit/models/TicketModel.java
+++ b/src/main/java/com/gitblit/models/TicketModel.java
@@ -43,6 +43,8 @@ import java.util.regex.Pattern;
import org.eclipse.jgit.util.RelativeDateFormatter;
+import com.gitblit.Constants;
+
/**
* The Gitblit Ticket model, its component classes, and enums.
*
@@ -773,10 +775,10 @@ public class TicketModel implements Serializable, Comparable<TicketModel> {
}
try {
- Pattern mentions = Pattern.compile("\\s@([A-Za-z0-9-_]+)");
+ Pattern mentions = Pattern.compile(Constants.REGEX_TICKET_MENTION);
Matcher m = mentions.matcher(text);
while (m.find()) {
- String username = m.group(1);
+ String username = m.group("user");
plusList(Field.mentions, username);
}
} catch (Exception e) {
diff --git a/src/main/java/com/gitblit/models/UserModel.java b/src/main/java/com/gitblit/models/UserModel.java
index e1522748..1d9e413b 100644
--- a/src/main/java/com/gitblit/models/UserModel.java
+++ b/src/main/java/com/gitblit/models/UserModel.java
@@ -36,6 +36,7 @@ import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ModelUtils;
+import com.gitblit.utils.SecureRandom;
import com.gitblit.utils.StringUtils;
/**
@@ -52,6 +53,8 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
public static final UserModel ANONYMOUS = new UserModel();
+ private static final SecureRandom RANDOM = new SecureRandom();
+
// field names are reflectively mapped in EditUser page
public String username;
public String password;
@@ -660,4 +663,8 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
String projectPath = StringUtils.getFirstPathElement(repository);
return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase(getPersonalPath());
}
+
+ public String createCookie() {
+ return StringUtils.getSHA1(RANDOM.randomBytes(32));
+ }
}
diff --git a/src/main/java/com/gitblit/service/MailService.java b/src/main/java/com/gitblit/service/MailService.java
index ec3a84ca..58acc9c0 100644
--- a/src/main/java/com/gitblit/service/MailService.java
+++ b/src/main/java/com/gitblit/service/MailService.java
@@ -17,6 +17,7 @@ package com.gitblit.service;
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
@@ -31,6 +32,7 @@ import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
+import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
@@ -272,9 +274,22 @@ public class MailService implements Runnable {
while ((message = queue.poll()) != null) {
try {
if (settings.getBoolean(Keys.mail.debug, false)) {
- logger.info("send: " + StringUtils.trimString(message.getSubject(), 60));
+ logger.info("send: '" + StringUtils.trimString(message.getSubject(), 60)
+ + "' to:" + StringUtils.trimString(Arrays.toString(message.getAllRecipients()), 300));
}
Transport.send(message);
+ } catch (SendFailedException sfe) {
+ if (settings.getBoolean(Keys.mail.debug, false)) {
+ logger.error("Failed to send message: {}", sfe.getMessage());
+ logger.info(" Invalid addresses: {}", Arrays.toString(sfe.getInvalidAddresses()));
+ logger.info(" Valid sent addresses: {}", Arrays.toString(sfe.getValidSentAddresses()));
+ logger.info(" Valid unset addresses: {}", Arrays.toString(sfe.getValidUnsentAddresses()));
+ logger.info("", sfe);
+ }
+ else {
+ logger.error("Failed to send message: {}", sfe.getMessage(), sfe.getNextException());
+ }
+ failures.add(message);
} catch (Throwable e) {
logger.error("Failed to send message", e);
failures.add(message);
diff --git a/src/main/java/com/gitblit/tickets/TicketNotifier.java b/src/main/java/com/gitblit/tickets/TicketNotifier.java
index 8c7fe6d4..b913db25 100644
--- a/src/main/java/com/gitblit/tickets/TicketNotifier.java
+++ b/src/main/java/com/gitblit/tickets/TicketNotifier.java
@@ -573,10 +573,10 @@ public class TicketNotifier {
// cc users mentioned in last comment
Change lastChange = ticket.changes.get(ticket.changes.size() - 1);
if (lastChange.hasComment()) {
- Pattern p = Pattern.compile("\\s@([A-Za-z0-9-_]+)");
+ Pattern p = Pattern.compile(Constants.REGEX_TICKET_MENTION);
Matcher m = p.matcher(lastChange.comment.text);
while (m.find()) {
- String username = m.group();
+ String username = m.group("user");
ccs.add(username);
}
}
diff --git a/src/main/java/com/gitblit/utils/ArrayUtils.java b/src/main/java/com/gitblit/utils/ArrayUtils.java
index 1402ad5e..b850ccc9 100644
--- a/src/main/java/com/gitblit/utils/ArrayUtils.java
+++ b/src/main/java/com/gitblit/utils/ArrayUtils.java
@@ -42,7 +42,7 @@ public class ArrayUtils {
}
public static boolean isEmpty(Collection<?> collection) {
- return collection == null || collection.size() == 0;
+ return collection == null || collection.isEmpty();
}
public static String toString(Collection<?> collection) {
diff --git a/src/main/java/com/gitblit/utils/CommitCache.java b/src/main/java/com/gitblit/utils/CommitCache.java
index a3963f50..53b8de19 100644
--- a/src/main/java/com/gitblit/utils/CommitCache.java
+++ b/src/main/java/com/gitblit/utils/CommitCache.java
@@ -19,9 +19,9 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.ObjectId;
@@ -58,7 +58,7 @@ public class CommitCache {
}
protected CommitCache() {
- cache = new ConcurrentHashMap<String, ObjectCache<List<RepositoryCommit>>>();
+ cache = new HashMap<>();
}
/**
@@ -93,7 +93,9 @@ public class CommitCache {
*
*/
public void clear() {
- cache.clear();
+ synchronized (cache) {
+ cache.clear();
+ }
}
/**
@@ -103,8 +105,11 @@ public class CommitCache {
*/
public void clear(String repositoryName) {
String repoKey = repositoryName.toLowerCase();
- ObjectCache<List<RepositoryCommit>> repoCache = cache.remove(repoKey);
- if (repoCache != null) {
+ boolean hadEntries = false;
+ synchronized (cache) {
+ hadEntries = cache.remove(repoKey) != null;
+ }
+ if (hadEntries) {
logger.info(MessageFormat.format("{0} commit cache cleared", repositoryName));
}
}
@@ -117,13 +122,17 @@ public class CommitCache {
*/
public void clear(String repositoryName, String branch) {
String repoKey = repositoryName.toLowerCase();
- ObjectCache<List<RepositoryCommit>> repoCache = cache.get(repoKey);
- if (repoCache != null) {
- List<RepositoryCommit> commits = repoCache.remove(branch.toLowerCase());
- if (!ArrayUtils.isEmpty(commits)) {
- logger.info(MessageFormat.format("{0}:{1} commit cache cleared", repositoryName, branch));
+ boolean hadEntries = false;
+ synchronized (cache) {
+ ObjectCache<List<RepositoryCommit>> repoCache = cache.get(repoKey);
+ if (repoCache != null) {
+ List<RepositoryCommit> commits = repoCache.remove(branch.toLowerCase());
+ hadEntries = !ArrayUtils.isEmpty(commits);
}
}
+ if (hadEntries) {
+ logger.info(MessageFormat.format("{0}:{1} commit cache cleared", repositoryName, branch));
+ }
}
/**
@@ -156,49 +165,55 @@ public class CommitCache {
if (cacheDays > 0 && (sinceDate.getTime() >= cacheCutoffDate.getTime())) {
// request fits within the cache window
String repoKey = repositoryName.toLowerCase();
- if (!cache.containsKey(repoKey)) {
- cache.put(repoKey, new ObjectCache<List<RepositoryCommit>>());
- }
-
- ObjectCache<List<RepositoryCommit>> repoCache = cache.get(repoKey);
String branchKey = branch.toLowerCase();
RevCommit tip = JGitUtils.getCommit(repository, branch);
Date tipDate = JGitUtils.getCommitDate(tip);
- List<RepositoryCommit> commits;
- if (!repoCache.hasCurrent(branchKey, tipDate)) {
- commits = repoCache.getObject(branchKey);
- if (ArrayUtils.isEmpty(commits)) {
- // we don't have any cached commits for this branch, reload
- commits = get(repositoryName, repository, branch, cacheCutoffDate);
- repoCache.updateObject(branchKey, tipDate, commits);
- logger.debug(MessageFormat.format("parsed {0} commits from {1}:{2} since {3,date,yyyy-MM-dd} in {4} msecs",
- commits.size(), repositoryName, branch, cacheCutoffDate, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
- } else {
- // incrementally update cache since the last cached commit
- ObjectId sinceCommit = commits.get(0).getId();
- List<RepositoryCommit> incremental = get(repositoryName, repository, branch, sinceCommit);
- logger.info(MessageFormat.format("incrementally added {0} commits to cache for {1}:{2} in {3} msecs",
- incremental.size(), repositoryName, branch, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
- incremental.addAll(commits);
- repoCache.updateObject(branchKey, tipDate, incremental);
- commits = incremental;
+ ObjectCache<List<RepositoryCommit>> repoCache;
+ synchronized (cache) {
+ repoCache = cache.get(repoKey);
+ if (repoCache == null) {
+ repoCache = new ObjectCache<>();
+ cache.put(repoKey, repoCache);
}
- } else {
- // cache is current
- commits = repoCache.getObject(branchKey);
- // evict older commits outside the cache window
- commits = reduce(commits, cacheCutoffDate);
- // update cache
- repoCache.updateObject(branchKey, tipDate, commits);
}
+ synchronized (repoCache) {
+ List<RepositoryCommit> commits;
+ if (!repoCache.hasCurrent(branchKey, tipDate)) {
+ commits = repoCache.getObject(branchKey);
+ if (ArrayUtils.isEmpty(commits)) {
+ // we don't have any cached commits for this branch, reload
+ commits = get(repositoryName, repository, branch, cacheCutoffDate);
+ repoCache.updateObject(branchKey, tipDate, commits);
+ logger.debug(MessageFormat.format("parsed {0} commits from {1}:{2} since {3,date,yyyy-MM-dd} in {4} msecs",
+ commits.size(), repositoryName, branch, cacheCutoffDate, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
+ } else {
+ // incrementally update cache since the last cached commit
+ ObjectId sinceCommit = commits.get(0).getId();
+ List<RepositoryCommit> incremental = get(repositoryName, repository, branch, sinceCommit);
+ logger.info(MessageFormat.format("incrementally added {0} commits to cache for {1}:{2} in {3} msecs",
+ incremental.size(), repositoryName, branch, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
+ incremental.addAll(commits);
+ repoCache.updateObject(branchKey, tipDate, incremental);
+ commits = incremental;
+ }
+ } else {
+ // cache is current
+ commits = repoCache.getObject(branchKey);
+ // evict older commits outside the cache window
+ commits = reduce(commits, cacheCutoffDate);
+ // update cache
+ repoCache.updateObject(branchKey, tipDate, commits);
+ }
- if (sinceDate.equals(cacheCutoffDate)) {
- list = commits;
- } else {
- // reduce the commits to those since the specified date
- list = reduce(commits, sinceDate);
+ if (sinceDate.equals(cacheCutoffDate)) {
+ // Mustn't hand out the cached list; that's not thread-safe
+ list = new ArrayList<>(commits);
+ } else {
+ // reduce the commits to those since the specified date
+ list = reduce(commits, sinceDate);
+ }
}
logger.debug(MessageFormat.format("retrieved {0} commits from cache of {1}:{2} since {3,date,yyyy-MM-dd} in {4} msecs",
list.size(), repositoryName, branch, sinceDate, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
@@ -222,8 +237,9 @@ public class CommitCache {
*/
protected List<RepositoryCommit> get(String repositoryName, Repository repository, String branch, Date sinceDate) {
Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, false);
- List<RepositoryCommit> commits = new ArrayList<RepositoryCommit>();
- for (RevCommit commit : JGitUtils.getRevLog(repository, branch, sinceDate)) {
+ List<RevCommit> revLog = JGitUtils.getRevLog(repository, branch, sinceDate);
+ List<RepositoryCommit> commits = new ArrayList<RepositoryCommit>(revLog.size());
+ for (RevCommit commit : revLog) {
RepositoryCommit commitModel = new RepositoryCommit(repositoryName, branch, commit);
List<RefModel> commitRefs = allRefs.get(commitModel.getId());
commitModel.setRefs(commitRefs);
@@ -243,8 +259,9 @@ public class CommitCache {
*/
protected List<RepositoryCommit> get(String repositoryName, Repository repository, String branch, ObjectId sinceCommit) {
Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, false);
- List<RepositoryCommit> commits = new ArrayList<RepositoryCommit>();
- for (RevCommit commit : JGitUtils.getRevLog(repository, sinceCommit.getName(), branch)) {
+ List<RevCommit> revLog = JGitUtils.getRevLog(repository, sinceCommit.getName(), branch);
+ List<RepositoryCommit> commits = new ArrayList<RepositoryCommit>(revLog.size());
+ for (RevCommit commit : revLog) {
RepositoryCommit commitModel = new RepositoryCommit(repositoryName, branch, commit);
List<RefModel> commitRefs = allRefs.get(commitModel.getId());
commitModel.setRefs(commitRefs);
@@ -261,7 +278,7 @@ public class CommitCache {
* @return a list of commits
*/
protected List<RepositoryCommit> reduce(List<RepositoryCommit> commits, Date sinceDate) {
- List<RepositoryCommit> filtered = new ArrayList<RepositoryCommit>();
+ List<RepositoryCommit> filtered = new ArrayList<RepositoryCommit>(commits.size());
for (RepositoryCommit commit : commits) {
if (commit.getCommitDate().compareTo(sinceDate) >= 0) {
filtered.add(commit);
diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java
index a02fc3ff..0eea1d61 100644
--- a/src/main/java/com/gitblit/utils/JGitUtils.java
+++ b/src/main/java/com/gitblit/utils/JGitUtils.java
@@ -99,6 +99,7 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.gitblit.Constants.MergeType;
import com.gitblit.GitBlitException;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
@@ -2453,44 +2454,13 @@ public class JGitUtils {
* @param repository
* @param src
* @param toBranch
+ * @param mergeType
+ * Defines the integration strategy to use for merging.
* @return true if we can merge without conflict
*/
- public static MergeStatus canMerge(Repository repository, String src, String toBranch) {
- RevWalk revWalk = null;
- try {
- revWalk = new RevWalk(repository);
- ObjectId branchId = repository.resolve(toBranch);
- if (branchId == null) {
- return MergeStatus.MISSING_INTEGRATION_BRANCH;
- }
- ObjectId srcId = repository.resolve(src);
- if (srcId == null) {
- return MergeStatus.MISSING_SRC_BRANCH;
- }
- RevCommit branchTip = revWalk.lookupCommit(branchId);
- RevCommit srcTip = revWalk.lookupCommit(srcId);
- if (revWalk.isMergedInto(srcTip, branchTip)) {
- // already merged
- return MergeStatus.ALREADY_MERGED;
- } else if (revWalk.isMergedInto(branchTip, srcTip)) {
- // fast-forward
- return MergeStatus.MERGEABLE;
- }
- RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
- boolean canMerge = merger.merge(branchTip, srcTip);
- if (canMerge) {
- return MergeStatus.MERGEABLE;
- }
- } catch (NullPointerException e) {
- LOGGER.error("Failed to determine canMerge", e);
- } catch (IOException e) {
- LOGGER.error("Failed to determine canMerge", e);
- } finally {
- if (revWalk != null) {
- revWalk.close();
- }
- }
- return MergeStatus.NOT_MERGEABLE;
+ public static MergeStatus canMerge(Repository repository, String src, String toBranch, MergeType mergeType) {
+ IntegrationStrategy strategy = IntegrationStrategyFactory.create(mergeType, repository, src, toBranch);
+ return strategy.canMerge();
}
@@ -2511,11 +2481,13 @@ public class JGitUtils {
* @param repository
* @param src
* @param toBranch
+ * @param mergeType
+ * Defines the integration strategy to use for merging.
* @param committer
* @param message
* @return the merge result
*/
- public static MergeResult merge(Repository repository, String src, String toBranch,
+ public static MergeResult merge(Repository repository, String src, String toBranch, MergeType mergeType,
PersonIdent committer, String message) {
if (!toBranch.startsWith(Constants.R_REFS)) {
@@ -2523,15 +2495,202 @@ public class JGitUtils {
toBranch = Constants.R_HEADS + toBranch;
}
- RevWalk revWalk = null;
+ IntegrationStrategy strategy = IntegrationStrategyFactory.create(mergeType, repository, src, toBranch);
+ MergeResult mergeResult = strategy.merge(committer, message);
+
+ if (mergeResult.status != MergeStatus.MERGED) {
+ return mergeResult;
+ }
+
try {
- revWalk = new RevWalk(repository);
- RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
- RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
- if (revWalk.isMergedInto(srcTip, branchTip)) {
- // already merged
- return new MergeResult(MergeStatus.ALREADY_MERGED, null);
+ // Update the integration branch ref
+ RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
+ mergeRefUpdate.setNewObjectId(strategy.getMergeCommit());
+ mergeRefUpdate.setRefLogMessage(strategy.getRefLogMessage(), false);
+ mergeRefUpdate.setExpectedOldObjectId(strategy.branchTip);
+ RefUpdate.Result rc = mergeRefUpdate.update();
+ switch (rc) {
+ case FAST_FORWARD:
+ // successful, clean merge
+ break;
+ default:
+ mergeResult = new MergeResult(MergeStatus.FAILED, null);
+ throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when {1} in {2}",
+ rc.name(), strategy.getOperationMessage(), repository.getDirectory()));
+ }
+ } catch (IOException e) {
+ LOGGER.error("Failed to merge", e);
+ }
+
+ return mergeResult;
+ }
+
+
+ private static abstract class IntegrationStrategy {
+ Repository repository;
+ String src;
+ String toBranch;
+
+ RevWalk revWalk;
+ RevCommit branchTip;
+ RevCommit srcTip;
+
+ RevCommit mergeCommit;
+ String refLogMessage;
+ String operationMessage;
+
+ RevCommit getMergeCommit() {
+ return mergeCommit;
+ }
+
+ String getRefLogMessage() {
+ return refLogMessage;
+ }
+
+ String getOperationMessage() {
+ return operationMessage;
+ }
+
+ IntegrationStrategy(Repository repository, String src, String toBranch) {
+ this.repository = repository;
+ this.src = src;
+ this.toBranch = toBranch;
+ }
+
+ void prepare() throws IOException {
+ if (revWalk == null) revWalk = new RevWalk(repository);
+ ObjectId branchId = repository.resolve(toBranch);
+ if (branchId != null) {
+ branchTip = revWalk.lookupCommit(branchId);
+ }
+ ObjectId srcId = repository.resolve(src);
+ if (srcId != null) {
+ srcTip = revWalk.lookupCommit(srcId);
+ }
+ }
+
+
+ abstract MergeStatus _canMerge() throws IOException;
+
+
+ MergeStatus canMerge() {
+ try {
+ prepare();
+ if (branchTip == null) {
+ return MergeStatus.MISSING_INTEGRATION_BRANCH;
+ }
+ if (srcTip == null) {
+ return MergeStatus.MISSING_SRC_BRANCH;
+ }
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return MergeStatus.ALREADY_MERGED;
+ }
+ // determined by specific integration strategy
+ return _canMerge();
+
+ } catch (NullPointerException e) {
+ LOGGER.error("Failed to determine canMerge", e);
+ } catch (IOException e) {
+ LOGGER.error("Failed to determine canMerge", e);
+ } finally {
+ if (revWalk != null) {
+ revWalk.close();
+ }
+ }
+
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+
+ abstract MergeResult _merge(PersonIdent committer, String message) throws IOException;
+
+
+ MergeResult merge(PersonIdent committer, String message) {
+ try {
+ prepare();
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return new MergeResult(MergeStatus.ALREADY_MERGED, null);
+ }
+ // determined by specific integration strategy
+ return _merge(committer, message);
+
+ } catch (IOException e) {
+ LOGGER.error("Failed to merge", e);
+ } finally {
+ if (revWalk != null) {
+ revWalk.close();
+ }
+ }
+
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
+ }
+
+
+ private static class FastForwardOnly extends IntegrationStrategy {
+ FastForwardOnly(Repository repository, String src, String toBranch) {
+ super(repository, src, toBranch);
+ }
+
+ @Override
+ MergeStatus _canMerge() throws IOException {
+ if (revWalk.isMergedInto(branchTip, srcTip)) {
+ // fast-forward
+ return MergeStatus.MERGEABLE;
+ }
+
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+ @Override
+ MergeResult _merge(PersonIdent committer, String message) throws IOException {
+ if (! revWalk.isMergedInto(branchTip, srcTip)) {
+ // is not fast-forward
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
+
+ mergeCommit = srcTip;
+ refLogMessage = "merge " + src + ": Fast-forward";
+ operationMessage = MessageFormat.format("fast-forwarding {0} to commit {1}", srcTip.getName(), branchTip.getName());
+
+ return new MergeResult(MergeStatus.MERGED, srcTip.getName());
+ }
+ }
+
+ private static class MergeIfNecessary extends IntegrationStrategy {
+ MergeIfNecessary(Repository repository, String src, String toBranch) {
+ super(repository, src, toBranch);
+ }
+
+ @Override
+ MergeStatus _canMerge() throws IOException {
+ if (revWalk.isMergedInto(branchTip, srcTip)) {
+ // fast-forward
+ return MergeStatus.MERGEABLE;
+ }
+
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean canMerge = merger.merge(branchTip, srcTip);
+ if (canMerge) {
+ return MergeStatus.MERGEABLE;
+ }
+
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+ @Override
+ MergeResult _merge(PersonIdent committer, String message) throws IOException {
+ if (revWalk.isMergedInto(branchTip, srcTip)) {
+ // fast-forward
+ mergeCommit = srcTip;
+ refLogMessage = "merge " + src + ": Fast-forward";
+ operationMessage = MessageFormat.format("fast-forwarding {0} to commit {1}", branchTip.getName(), srcTip.getName());
+
+ return new MergeResult(MergeStatus.MERGED, srcTip.getName());
}
+
RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
boolean merged = merger.merge(branchTip, srcTip);
if (merged) {
@@ -2555,20 +2714,64 @@ public class JGitUtils {
ObjectId mergeCommitId = odi.insert(commitBuilder);
odi.flush();
- // set the merge ref to the merge commit
- RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId);
- RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
- mergeRefUpdate.setNewObjectId(mergeCommitId);
- mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false);
- RefUpdate.Result rc = mergeRefUpdate.update();
- switch (rc) {
- case FAST_FORWARD:
- // successful, clean merge
- break;
- default:
- throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}",
- rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory()));
+ mergeCommit = revWalk.parseCommit(mergeCommitId);
+ refLogMessage = "commit: " + mergeCommit.getShortMessage();
+ operationMessage = MessageFormat.format("merging commit {0} into {1}", srcTip.getName(), branchTip.getName());
+
+ // return the merge commit id
+ return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
+ } finally {
+ odi.close();
+ }
+ }
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
+ }
+
+ private static class MergeAlways extends IntegrationStrategy {
+ MergeAlways(Repository repository, String src, String toBranch) {
+ super(repository, src, toBranch);
+ }
+
+ @Override
+ MergeStatus _canMerge() throws IOException {
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean canMerge = merger.merge(branchTip, srcTip);
+ if (canMerge) {
+ return MergeStatus.MERGEABLE;
+ }
+
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+ @Override
+ MergeResult _merge(PersonIdent committer, String message) throws IOException {
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean merged = merger.merge(branchTip, srcTip);
+ if (merged) {
+ // create a merge commit and a reference to track the merge commit
+ ObjectId treeId = merger.getResultTreeId();
+ ObjectInserter odi = repository.newObjectInserter();
+ try {
+ // Create a commit object
+ CommitBuilder commitBuilder = new CommitBuilder();
+ commitBuilder.setCommitter(committer);
+ commitBuilder.setAuthor(committer);
+ commitBuilder.setEncoding(Constants.CHARSET);
+ if (StringUtils.isEmpty(message)) {
+ message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());
}
+ commitBuilder.setMessage(message);
+ commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());
+ commitBuilder.setTreeId(treeId);
+
+ // Insert the merge commit into the repository
+ ObjectId mergeCommitId = odi.insert(commitBuilder);
+ odi.flush();
+
+ mergeCommit = revWalk.parseCommit(mergeCommitId);
+ refLogMessage = "commit: " + mergeCommit.getShortMessage();
+ operationMessage = MessageFormat.format("merging commit {0} into {1}", srcTip.getName(), branchTip.getName());
// return the merge commit id
return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
@@ -2576,17 +2779,27 @@ public class JGitUtils {
odi.close();
}
}
- } catch (IOException e) {
- LOGGER.error("Failed to merge", e);
- } finally {
- if (revWalk != null) {
- revWalk.close();
+
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
+ }
+
+
+ private static class IntegrationStrategyFactory {
+ static IntegrationStrategy create(MergeType mergeType, Repository repository, String src, String toBranch) {
+ switch(mergeType) {
+ case FAST_FORWARD_ONLY:
+ return new FastForwardOnly(repository, src, toBranch);
+ case MERGE_IF_NECESSARY:
+ return new MergeIfNecessary(repository, src, toBranch);
+ case MERGE_ALWAYS:
+ return new MergeAlways(repository, src, toBranch);
}
+ return null;
}
- return new MergeResult(MergeStatus.FAILED, null);
}
-
-
+
+
/**
* Returns the LFS URL for the given oid
* Currently assumes that the Gitblit Filestore is used
diff --git a/src/main/java/com/gitblit/utils/MarkdownUtils.java b/src/main/java/com/gitblit/utils/MarkdownUtils.java
index e0c9dd4e..8371b3c6 100644
--- a/src/main/java/com/gitblit/utils/MarkdownUtils.java
+++ b/src/main/java/com/gitblit/utils/MarkdownUtils.java
@@ -30,6 +30,7 @@ import org.pegdown.ParsingTimeoutException;
import org.pegdown.PegDownProcessor;
import org.pegdown.ast.RootNode;
+import com.gitblit.Constants;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.wicket.MarkupProcessor.WorkaroundHtmlSerializer;
@@ -137,8 +138,8 @@ public class MarkdownUtils {
String canonicalUrl = settings.getString(Keys.web.canonicalUrl, "https://localhost:8443");
// emphasize and link mentions
- String mentionReplacement = String.format(" **[@$1](%1s/user/$1)**", canonicalUrl);
- text = text.replaceAll("\\s@([A-Za-z0-9-_]+)", mentionReplacement);
+ String mentionReplacement = String.format("**[@${user}](%1s/user/${user})**", canonicalUrl);
+ text = text.replaceAll(Constants.REGEX_TICKET_MENTION, mentionReplacement);
// link ticket refs
String ticketReplacement = MessageFormat.format("$1[#$2]({0}/tickets?r={1}&h=$2)$3", canonicalUrl, repositoryName);
diff --git a/src/main/java/com/gitblit/utils/SecureRandom.java b/src/main/java/com/gitblit/utils/SecureRandom.java
new file mode 100644
index 00000000..119533d4
--- /dev/null
+++ b/src/main/java/com/gitblit/utils/SecureRandom.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 gitblit.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.utils;
+
+/**
+ * Wrapper class for java.security.SecureRandom, which will periodically reseed
+ * the PRNG in case an instance of the class has been running for a long time.
+ *
+ * @author Florian Zschocke
+ */
+public class SecureRandom {
+
+ /** Period (in ms) after which a new SecureRandom will be created in order to get a fresh random seed. */
+ private static final long RESEED_PERIOD = 24 * 60 * 60 * 1000; /* 24 hours */
+
+
+ private long last;
+ private java.security.SecureRandom random;
+
+
+
+ public SecureRandom() {
+ // Make sure the SecureRandom is seeded right from the start.
+ // This also lets any blocks during seeding occur at creation
+ // and prevents it from happening when getting next random bytes.
+ seed();
+ }
+
+
+
+ public byte[] randomBytes(int num) {
+ byte[] bytes = new byte[num];
+ nextBytes(bytes);
+ return bytes;
+ }
+
+
+ public void nextBytes(byte[] bytes) {
+ random.nextBytes(bytes);
+ reseed(false);
+ }
+
+
+ void reseed(boolean forced) {
+ long ts = System.currentTimeMillis();
+ if (forced || (ts - last) > RESEED_PERIOD) {
+ last = ts;
+ runReseed();
+ }
+ }
+
+
+
+ private void seed() {
+ random = new java.security.SecureRandom();
+ random.nextBytes(new byte[0]);
+ last = System.currentTimeMillis();
+ }
+
+
+ private void runReseed() {
+ // Have some other thread hit the penalty potentially incurred by reseeding,
+ // so that we can immediately return and not block the operation in progress.
+ new Thread() {
+ public void run() {
+ seed();
+ }
+ }.start();
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index a215b4d6..b3cbef82 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -660,6 +660,7 @@ gb.nTotalTickets = {0} total
gb.body = body
gb.mergeSha = mergeSha
gb.mergeTo = merge to
+gb.mergeType = merge type
gb.labels = labels
gb.reviewers = reviewers
gb.voters = voters
@@ -671,6 +672,7 @@ gb.repositoryDoesNotAcceptPatchsets = This repository does not accept patchsets.
gb.serverDoesNotAcceptPatchsets = This server does not accept patchsets.
gb.ticketIsClosed = This ticket is closed.
gb.mergeToDescription = default integration branch for merging ticket patchsets
+gb.mergeTypeDescription = merge a ticket fast-forward only, if necessary, or always with a merge commit to the integration branch
gb.anonymousCanNotPropose = Anonymous users can not propose patchsets.
gb.youDoNotHaveClonePermission = You are not permitted to clone this repository.
gb.myTickets = my tickets
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_nl.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_nl.properties
index f43b8f52..f71d67d7 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_nl.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_nl.properties
@@ -476,7 +476,7 @@ gb.oneMoreCommit = 1 commit \u00bb
gb.pushedNewTag = push nieuwe tag
gb.createdNewTag = nieuwe tag gemaakt
gb.deletedTag = tag verwijderd
-gb.pushedNewBranch = push neuwe branch
+gb.pushedNewBranch = push nieuwe branch
gb.createdNewBranch = nieuwe branch gemaakt
gb.deletedBranch = branch verwijderd
gb.createdNewPullRequest = pull verzoek gemaakt
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_TW.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_TW.properties
index 16a9c864..bf2d2c32 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_TW.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_TW.properties
@@ -1,772 +1,783 @@
#!
-#! created/edited by Popeye version 0.54 (popeye.sourceforge.net)
+#! created/edited by Popeye version 0.55 (https://github.com/koppor/popeye)
#! encoding:ISO-8859-1
-gb.about = \u95dc\u65bc
-gb.acceptNewPatchsets = \u5141\u8a31\u88dc\u4e01
-gb.acceptNewPatchsetsDescription = \u63a5\u53d7\u5230\u7248\u672c\u5009\u9032\u884c\u4fee\u88dc\u52d5\u4f5c
-gb.acceptNewTickets = \u5141\u8a31\u5efa\u7acb\u4efb\u52d9\u55ae
-gb.acceptNewTicketsDescription = \u5141\u8a31\u65b0\u589e"\u81ed\u87f2","\u512a\u5316","\u4efb\u52d9"\u5404\u985e\u578b\u4efb\u52d9\u55ae
-gb.accessDenied = \u62d2\u7d55\u5b58\u53d6
-gb.accessLevel = \u5b58\u53d6\u7b49\u7d1a
-gb.accessPermissions = \u5b58\u53d6\u6b0a\u9650
-gb.accessPermissionsDescription = restrict access by users and teams
-gb.accessPermissionsForTeamDescription = set team members and grant access to specific restricted repositories
-gb.accessPermissionsForUserDescription = set team memberships or grant access to specific restricted repositories
-gb.accessPolicy = \u5b58\u53d6\u653f\u7b56
-gb.accessPolicyDescription = \u9078\u64c7\u7528\u4f86\u63a7\u5236\u6587\u4ef6\u5eab\u7684\u5b58\u53d6\u653f\u7b56\u4ee5\u53ca\u6b0a\u9650\u8a2d\u5b9a
-gb.accessRestriction = \u9650\u5236\u5b58\u53d6
-gb.accountPreferences = \u5e33\u865f\u8a2d\u5b9a
-gb.accountPreferencesDescription = \u8a2d\u5b9a\u5e33\u865f\u9810\u8a2d\u503c
-gb.action = \u52d5\u4f5c
-gb.active = \u6d3b\u52d5
-gb.activeAuthors = \u6d3b\u8e8d\u7528\u6236
-gb.activeRepositories = \u6d3b\u8e8d\u7248\u672c\u5eab
-gb.activity = \u6d3b\u52d5
-gb.add = \u65b0\u589e
-gb.addComment = \u65b0\u589e\u8a3b\u89e3
-gb.addedNCommits = {0}\u500b\u6a94\u6848\u63d0\u4ea4\u5b8c\u7562
-gb.addedOneCommit = \u63d0\u4ea41\u500b\u6a94\u6848
-gb.addition = addition
-gb.addSshKey = \u65b0\u589e SSH Key
-gb.administration = \u7ba1\u7406\u6b0a\u9650
-gb.administrator = \u7ba1\u7406\u54e1
-gb.administratorPermission = Gitblit \u7ba1\u7406\u54e1
-gb.affiliationChanged = affiliation changed
-gb.age = \u6642\u9593
-gb.all = \u5168\u90e8
-gb.allBranches = \u6240\u6709\u5206\u652f
-gb.allowAuthenticatedDescription = \u6279\u51c6 RW+ \u6b0a\u9650\u7d66\u4e88\u5c08\u6848\u6210\u54e1
-gb.allowForks = \u5141\u8a31\u5efa\u7acb\u5206\u652f(forks)
-gb.allowForksDescription = \u5141\u8a31\u5df2\u6388\u6b0a\u7684\u4f7f\u7528\u8005\u5f9e\u6587\u4ef6\u5eab\u5efa\u7acb\u5206\u652f(fork)
-gb.allowNamedDescription = grant fine-grained permissions to named users or teams
-gb.allProjects = \u5168\u90e8\u7fa4\u7d44
-gb.allTags = \u6240\u6709\u6a19\u7c64
-gb.anonymousCanNotPropose = \u533f\u540d\u8005\u4e0d\u80fd\u63d0\u4f9b\u88dc\u4e01
-gb.anonymousPolicy = \u533f\u540d\u72c0\u614b\u53ef\u4ee5View, Clone\u8207Push
-gb.anonymousPolicyDescription = \u4efb\u4f55\u4eba\u53ef\u6aa2\u8996,\u8907\u88fd(clone)\u8207\u63a8\u9001(push)\u6587\u4ef6\u5230\u6587\u4ef6\u5eab
-gb.anonymousUser= \u533f\u540d\u72c0\u614b
-gb.any = \u4efb\u4f55
-gb.approve = \u901a\u904e
-gb.at = at
-gb.attributes = \u5c6c\u6027
-gb.authenticatedPushPolicy = Restrict Push (Authenticated)
-gb.authenticatedPushPolicyDescription = \u4efb\u4f55\u4eba\u53ef\u4ee5\u6aa2\u8996\u8207\u8907\u88fd(clone).\u6240\u6709\u6587\u4ef6\u5eab\u6210\u54e1\u7686\u6709RW+\u8207\u63a8\u9001(push)\u529f\u80fd.
+gb.repository = \u7248\u672c\u5eab
+gb.owner = \u64c1\u6709\u8005
+gb.description = \u6982\u8ff0
+gb.lastChange = \u6700\u8fd1\u4fee\u6539
+gb.refs = \u6bd4\u5c0d
+gb.tag = \u6a19\u7c64
+gb.tags = \u6a19\u7c64
gb.author = \u4f5c\u8005
-gb.authored = \u5df2\u6388\u6b0a
-gb.authorizationControl = \u6388\u6b0a\u7ba1\u63a7
-gb.available = \u53ef\u7528
-gb.blame = \u7a76\u67e5
-gb.blinkComparator = Blink comparator
-gb.blob = \u5340\u584a
-gb.body = body
-gb.bootDate = \u555f\u52d5\u65e5
-gb.branch = \u5206\u652f
+gb.committer = \u78ba\u8a8d\u8005
+gb.commit = \u63d0\u4ea4
+gb.age = \u6642\u9593
+gb.tree = \u76ee\u9304
+gb.parent = \u4e0a\u500b\u7248\u672c
+gb.url = URL
+gb.history = \u6b77\u53f2
+gb.raw = \u539f\u59cb
+gb.object = \u7269\u4ef6
+gb.ticketId = \u4efb\u52d9ID
+gb.ticketAssigned = \u5df2\u6307\u5b9a
+gb.ticketOpenDate = \u767c\u884c\u65e5
+gb.ticketComments = \u8a3b\u89e3
+gb.view = \u6aa2\u8996
+gb.local = \u672c\u5730\u7aef
+gb.remote = \u9060\u7aef
gb.branches = \u5206\u652f
-gb.branchStats = \u9019\u500b\u5206\u652f{2}\u6709{0}\u500b\u63d0\u4ea4\u4ee5\u53ca{1}\u500b\u6a19\u7c64
-gb.browse = \u700f\u89bd
-gb.bugTickets = \u81ed\u87f2
-gb.busyCollectingGarbage = \u62b1\u6b49,Gitblit\u6b63\u5728\u56de\u6536\u7cfb\u7d71\u8cc7\u6e90\u4e2d:{0}
-gb.byNAuthors = \u7d93\u7531{0}\u500b\u4f5c\u8005
-gb.byOneAuthor = \u7d93\u7531{0}
-gb.caCompromise = CA compromise
+gb.patch = \u4fee\u88dc\u6a94
+gb.diff = \u5dee\u7570
+gb.log = \u65e5\u8a8c
+gb.moreLogs = \u66f4\u591a\u63d0\u4ea4 ...
+gb.allTags = \u6240\u6709\u6a19\u7c64
+gb.allBranches = \u6240\u6709\u5206\u652f
+gb.summary = \u532f\u7e3d
+gb.ticket = \u4efb\u52d9
+gb.newRepository = \u5efa\u7acb\u7248\u672c\u5eab
+gb.newUser = \u5efa\u7acb\u4f7f\u7528\u8005
+gb.commitdiff = \u5dee\u7570
+gb.tickets = \u4efb\u52d9
+gb.pageFirst = \u7b2c\u4e00\u7b46
+gb.pagePrevious = \u4e0a\u4e00\u9801
+gb.pageNext = \u4e0b\u4e00\u9801
+gb.head = HEAD
+gb.blame = \u8a73\u67e5
+gb.login = \u767b\u5165
+gb.logout = \u767b\u51fa
+gb.username = \u4f7f\u7528\u8005\u540d\u7a31
+gb.password = \u5bc6\u78bc
+gb.tagger = \u6a19\u8a18\u8005
+gb.moreHistory = \u66f4\u591a\u6b77\u53f2\u7d00\u9304...
+gb.difftocurrent = \u6bd4\u5c0d\u5dee\u7570
+gb.search = \u641c\u5c0b
+gb.searchForAuthor = \u4f9d\u5be9\u6838\u8005\u641c\u5c0b\u63d0\u4ea4\u5167\u5bb9
+gb.searchForCommitter = \u4f9d\u63d0\u4ea4\u8005\u641c\u5c0b\u63d0\u4ea4\u5167\u5bb9
+gb.addition = \u589e\u52a0
+gb.modification = \u4fee\u6539
+gb.deletion = \u522a\u9664
+gb.rename = \u6539\u540d\u7a31
+gb.metrics = \u7d71\u8a08
+gb.stats = \u7d71\u8a08
+gb.markdown = markdown
+gb.changedFiles = \u5df2\u8b8a\u66f4\u904e\u7684\u6a94\u6848
+gb.filesAdded = \u65b0\u589e{0}\u500b\u6a94\u6848
+gb.filesModified = \u4fee\u6539{0}\u500b\u6a94\u6848
+gb.filesDeleted = \u522a\u9664{0}\u500b\u6a94\u6848
+gb.filesCopied = \u8907\u88fd{0}\u500b\u6a94\u6848
+gb.filesRenamed = \u4fee\u6539{0}\u500b\u6a94\u6848\u540d\u7a31
+gb.missingUsername = \u7121\u4f7f\u7528\u8005\u540d\u7a31
+gb.edit = \u7de8\u8f2f
+gb.searchTypeTooltip = \u9078\u64c7\u641c\u5c0b\u985e\u578b
+gb.searchTooltip = \u641c\u5c0b{0}
+gb.delete = \u522a\u9664
+gb.docs = \u6587\u4ef6
+gb.accessRestriction = \u9650\u5236\u5b58\u53d6
+gb.name = \u540d\u5b57
+gb.enableTickets = \u555f\u7528\u4efb\u52d9(Ticket)\u7cfb\u7d71
+gb.enableDocs = \u555f\u7528\u8aaa\u660e\u6587\u4ef6
+gb.save = \u5132\u5b58
+gb.showRemoteBranches = \u986f\u793a\u9060\u7aef\u5206\u652f
+gb.editUsers = \u4fee\u6539\u5e33\u865f
+gb.confirmPassword = \u78ba\u8a8d\u5bc6\u78bc
+gb.restrictedRepositories = \u53d7\u9650\u5236\u7684\u7248\u672c\u5eab
gb.canAdmin = \u53ef\u7ba1\u7406
+gb.notRestricted = \u533f\u540d\u72c0\u614b\u53ef\u4ee5View, Clone\u8207Push
+gb.pushRestricted = \u6709\u6388\u6b0a\u624d\u80fd\u63a8\u9001(push)
+gb.cloneRestricted = \u6709\u6388\u6b0a\u624d\u80fd\u8907\u88fd(clone)\u8207\u63a8\u9001(push)
+gb.viewRestricted = \u6709\u6388\u6b0a\u624d\u80fd\u6aa2\u8996(view),\u8907\u88fd(clone), \u8207\u63a8\u9001(push)
+gb.useTicketsDescription = readonly, distributed Ticgit issues
+gb.useDocsDescription = \u8a08\u7b97\u7248\u672c\u5eab\u88e1\u9762\u7684Markdown\u6a94\u6848
+gb.showRemoteBranchesDescription = \u986f\u793a\u9060\u7aef\u5206\u652f(branches)
gb.canAdminDescription = \u53ef\u7ba1\u7406Gitblit\u4f3a\u670d\u5668
+gb.permittedUsers = \u5141\u8a31\u7528\u6236
+gb.isFrozen = \u51cd\u7d50\u63a5\u6536
+gb.isFrozenDescription = \u7981\u6b62\u63a8\u9001(push)
+gb.zip = zip\u58d3\u7e2e\u6a94
+gb.showReadme = \u986f\u793areadme\u6587\u4ef6
+gb.showReadmeDescription = \u5728\u532f\u7e3d\u9801\u9762\u4e2d\u986f\u793a"readme"(markdown\u683c\u5f0f)
+gb.nameDescription = \u4f7f\u7528"/"\u505a\u70ba\u7248\u672c\u5eab\u7fa4\u7d44\u5206\u985e. \u5982: library/mycoolib.git
+gb.ownerDescription = \u64c1\u6709\u8005\u53ef\u4fee\u6539\u7248\u672c\u5eab\u8a2d\u5b9a\u503c
+gb.blob = \u5340\u584a
+gb.commitActivityTrend = \u63d0\u4ea4\u8da8\u52e2
+gb.commitActivityDOW = \u6bcf(\u65e5)\u9031\u63d0\u4ea4
+gb.commitActivityAuthors = \u63d0\u4ea4\u6d3b\u8e8d\u7387(\u4f7f\u7528\u8005)
+gb.feed = \u8cc7\u6599\u8a02\u95b1
gb.cancel = \u53d6\u6d88
-gb.canCreate = \u53ef\u5efa\u7acb
-gb.canCreateDescription = \u80fd\u5920\u5efa\u7acb\u79c1\u4eba\u6587\u4ef6\u5eab
-gb.canFork = \u53ef\u5efa\u7acb\u5206\u652f(fork)
-gb.canForkDescription = \u53ef\u4ee5\u5efa\u7acb\u6587\u4ef6\u5eab\u5206\u652f(fork),\u4e26\u4e14\u8907\u88fd\u5230\u79c1\u4eba\u6587\u4ef6\u5eab\u4e2d
-gb.canNotLoadRepository = \u7121\u6cd5\u8f09\u5165\u7248\u672c\u5eab
-gb.canNotProposePatchset = \u4e0d\u80fd\u63d0\u4f9b\u88dc\u4e01
-gb.certificate = \u8b49\u66f8
-gb.certificateRevoked = \u8b49\u66f8{0,number,0} \u5df2\u7d93\u88ab\u53d6\u6d88
-gb.certificates = \u8b49\u66f8
-gb.cessationOfOperation = cessation of operation
-gb.changedFiles = \u5df2\u8b8a\u66f4\u904e\u7684\u6a94\u6848
-gb.changedStatus = changed the status
gb.changePassword = \u4fee\u6539\u5bc6\u78bc
-gb.checkout = \u6aa2\u51fa(checkout)
-gb.checkoutStep1 = Fetch the current patchset \u2014 run this from your project directory
-gb.checkoutStep2 = \u5c07\u8a72\u88dc\u4e01\u8f49\u51fa\u5230\u65b0\u7684\u5206\u652f\u7136\u5f8c\u7528\u4f86\u6aa2\u8996
-gb.checkoutViaCommandLine = \u4f7f\u7528\u6307\u4ee4Checkout
-gb.checkoutViaCommandLineNote = \u4f60\u53ef\u4ee5\u5f9e\u4f60\u6587\u4ef6\u5eab\u4e2dcheckout\u4e00\u4efd,\u7136\u5f8c\u9032\u884c\u6e2c\u8a66
-gb.clearCache = \u6e05\u9664\u5feb\u53d6
-gb.clientCertificateBundleSent = {0}\u7684\u7528\u6236\u8b49\u66f8\u5df2\u5bc4\u767c
-gb.clientCertificateGenerated = \u6210\u529f\u7522\u751f{0}\u7684\u65b0\u8b49\u66f8
+gb.isFederated = \u5df2\u7d93federated
+gb.federateThis = \u8207\u6b64\u7248\u672c\u5eab federate
+gb.federateOrigin = federate the origin
+gb.excludeFromFederation = exclude from federation
+gb.excludeFromFederationDescription = \u7981\u6b62federated \u7684Gitblit\u4f3a\u670d\u5668
+gb.tokens = federation tokens
+gb.tokenAllDescription = \u6240\u6709\u7248\u672c\u5eab,\u4f7f\u7528\u8005\u8207\u8a2d\u5b9a
+gb.tokenUnrDescription = \u6240\u6709\u7248\u672c\u5eab\u8207\u4f7f\u7528\u8005
+gb.tokenJurDescription = \u6240\u6709\u7248\u672c\u5eab
+gb.federatedRepositoryDefinitions = \u7248\u672c\u5eab\u5b9a\u7fa9
+gb.federatedUserDefinitions = \u4f7f\u7528\u8005\u5b9a\u7fa9
+gb.federatedSettingDefinitions = setting definitions
+gb.proposals = federation proposals
+gb.received = \u5df2\u63a5\u6536
+gb.type = \u985e\u578b
+gb.token = token
+gb.repositories = \u7248\u672c\u5eab
+gb.proposal = \u63d0\u6848
+gb.frequency = \u983b\u7387
+gb.folder = \u76ee\u9304
+gb.lastPull = \u4e0a\u6b21\u4e0b\u8f09(pull)
+gb.nextPull = \u4e0b\u4e00\u500b pull
+gb.inclusions = inclusions
+gb.exclusions = \u6392\u9664
+gb.registration = \u8a3b\u518a
+gb.registrations = federation registrations
+gb.sendProposal = \u63d0\u6848
+gb.status = \u72c0\u614b
+gb.origin = origin
+gb.headRef = \u9810\u8a2d\u5206\u652f(HEAD)
+gb.headRefDescription = \u9810\u8a2d\u5206\u652f\u5c07\u6703\u8907\u88fd\u4ee5\u53ca\u986f\u793a\u5230\u532f\u7e3d\u9801\u9762
+gb.federationStrategy = federation \u7b56\u7565
+gb.federationRegistration = federation registration
+gb.federationResults = federation pull results
+gb.federationSets = federation sets
+gb.message = \u8a0a\u606f
+gb.myUrlDescription = \u60a8Gitblit\u4f3a\u670d\u5668\u7684\u516c\u958bURL
+gb.destinationUrl = \u50b3\u9001
+gb.destinationUrlDescription = \u50b3\u9001Gitblit\u9023\u7d50\u5230\u4f60\u7684\u5c08\u6848(proposal)
+gb.users = \u4f7f\u7528\u8005
+gb.federation = federation
+gb.error = \u932f\u8aa4
+gb.refresh = \u91cd\u6574
+gb.browse = \u700f\u89bd
gb.clone = \u8907\u88fd(clone)
-gb.clonePermission = {0} \u8907\u88fd(clone)
-gb.clonePolicy = Restrict Clone & Push
-gb.clonePolicyDescription = \u4efb\u4f55\u4eba\u53ef\u4ee5\u770b\u6587\u4ef6\u5eab. \u4f46\u4f60\u80fd\u5920\u8907\u88fd(clone)\u8207\u63a8\u9001(push)
-gb.cloneRestricted = authenticated clone & push
-gb.closeBrowser = \u8acb\u95dc\u9589\u700f\u89bd\u5668\u7d50\u675f\u6b64\u767b\u5165\u968e\u6bb5
-gb.closed = \u95dc\u9589
-gb.closedMilestones = \u5df2\u95dc\u9589\u7684\u91cc\u7a0b\u7891(milestones)
-gb.combinedMd5Rename = Gitblit\u4f7f\u7528md5\u65b9\u5f0f\u5c07\u5bc6\u78bc\u7de8\u78bc(\u7121\u6cd5\u9084\u539f).\u4f60\u5fc5\u9808\u8f38\u5165\u65b0\u5bc6\u78bc.
-gb.comment = \u8a3b\u89e3
-gb.commented = \u5df2\u8a3b\u89e3
-gb.comments = \u8a3b\u89e3
-gb.commit = \u63d0\u4ea4
-gb.commitActivityAuthors = \u63d0\u4ea4\u6d3b\u8e8d\u7387(\u4f7f\u7528\u8005)
-gb.commitActivityDOW = \u6bcf(\u65e5)\u9031\u63d0\u4ea4
-gb.commitActivityTrend = \u63d0\u4ea4\u8da8\u52e2\u5716
-gb.commitdiff = \u63d0\u4ea4\u5dee\u7570
-gb.commitIsNull = \u63d0\u4ea4\u5167\u5bb9\u662f\u7a7a\u7684
-gb.commitMessageRenderer = \u63d0\u4ea4\u8a0a\u606f\u5448\u73fe\u65b9\u5f0f
-gb.commitMessageRendererDescription = \u63d0\u4ea4\u8a0a\u606f\u53ef\u4ee5\u4f7f\u7528\u6587\u5b57\u6216\u662f\u6a19\u8a18\u8a9e\u8a00(markup)\u5448\u73fe
+gb.filter = \u7be9\u9078
+gb.create = \u5efa\u7acb
+gb.servers = \u4f3a\u670d\u5668
+gb.recent = \u6700\u8fd1
+gb.available = \u53ef\u7528
+gb.selected = \u9078\u5b9a
+gb.size = \u5bb9\u91cf
+gb.downloading = \u4e0b\u8f09\u4e2d
+gb.loading = \u8f09\u5165
+gb.starting = \u555f\u52d5\u4e2d
+gb.general = \u4e00\u822c
+gb.settings = \u8a2d\u5b9a
+gb.manage = \u7ba1\u7406
+gb.lastLogin = \u6700\u8fd1\u767b\u5165
+gb.skipSizeCalculation = \u7565\u904e\u5bb9\u91cf\u8a08\u7b97
+gb.skipSizeCalculationDescription = \u4e0d\u8a08\u7b97\u7248\u672c\u5eab\u5bb9\u91cf(\u52a0\u5feb\u7db2\u9801\u8f09\u5165\u901f\u5ea6)
+gb.skipSummaryMetrics = \u7565\u904e\u91cf\u5316\u532f\u7e3d
+gb.skipSummaryMetricsDescription = \u4e0d\u8981\u8a08\u7b97\u91cf\u5316\u4e26\u4e14\u986f\u793a\u5728\u532f\u7e3d\u9801\u9762\u4e0a(\u52a0\u5feb\u901f\u5ea6)
+gb.accessLevel = \u5b58\u53d6\u7b49\u7d1a
+gb.default = \u9810\u8a2d
+gb.setDefault = \u8a2d\u70ba\u9810\u8a2d\u503c
+gb.since = \u5f9e
+gb.bootDate = \u555f\u52d5\u65e5
+gb.servletContainer = servlet\u5bb9\u5668
+gb.heapMaximum = \u6700\u5927\u5806\u7a4d(heap)
+gb.heapAllocated = \u5df2\u4f7f\u7528\u5806\u7a4d(Heap)
+gb.heapUsed = \u5df2\u4f7f\u7528\u7684\u5806\u7a4d(heap)
+gb.free = \u91cb\u653e
+gb.version = \u7248\u672c
+gb.releaseDate = \u767c\u8868\u65e5
+gb.date = \u65e5\u671f
+gb.activity = \u6d3b\u52d5
+gb.subscribe = \u8a02\u95b1
+gb.branch = \u5206\u652f
+gb.maxHits = \u6700\u5927\u9ede\u64ca
+gb.recentActivity = \u6700\u8fd1\u6d3b\u8e8d\u72c0\u6cc1
+gb.recentActivityStats = \u904e\u53bb{0}\u5929,\u4e00\u5171\u6709{2}\u4eba\u505a\u4e86{1}\u4efd\u63d0\u4ea4
+gb.recentActivityNone = \u904e\u53bb{0}\u5929/\u7121
+gb.dailyActivity = \u6bcf\u65e5\u6d3b\u52d5
+gb.activeRepositories = \u6d3b\u8e8d\u7248\u672c\u5eab
+gb.activeAuthors = \u6d3b\u8e8d\u7528\u6236
gb.commits = \u63d0\u4ea4
-gb.commitsInPatchsetN = \u88dc\u4e01 {0} \u7684\u63d0\u4ea4
-gb.commitsTo = {0} commits to
+gb.teams = \u5718\u968a
+gb.teamName = \u5718\u968a\u540d\u7a31
+gb.teamMembers = \u5718\u968a\u6210\u54e1
+gb.teamMemberships = \u5718\u968a\u6210\u54e1(memberships)
+gb.newTeam = \u5efa\u7acb\u5718\u968a
+gb.permittedTeams = permitted teams
+gb.emptyRepository = \u7a7a\u7684\u7248\u672c\u5eab
+gb.repositoryUrl = \u7248\u672c\u5eab url
+gb.mailingLists = \u90f5\u4ef6\u540d\u55ae
+gb.preReceiveScripts = pre-receive \u8173\u672c
+gb.postReceiveScripts = post-receive\u8173\u672c
+gb.hookScripts = hook\u7684\u8173\u672c
+gb.customFields = custom fields
+gb.customFieldsDescription = custom fields available to Groovy hooks
+gb.accessPermissions = \u5b58\u53d6\u6b0a\u9650
+gb.filters = \u7be9\u9078
+gb.generalDescription = \u4e00\u822c\u8a2d\u5b9a
+gb.accessPermissionsDescription = restrict access by users and teams
+gb.accessPermissionsForUserDescription = set team memberships or grant access to specific restricted repositories
+gb.accessPermissionsForTeamDescription = set team members and grant access to specific restricted repositories
+gb.federationRepositoryDescription = \u8207\u5176\u4ed6gitblit\u4f3a\u670d\u5668\u5206\u4eab\u4e00\u8d77\u4f7f\u7528\u9019\u500b\u7248\u672c\u5eab
+gb.hookScriptsDescription = \u7576\u63a8\u9001(push)\u81f3\u6b64Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u6642, \u57f7\u884cGroovy\u8173\u672c
+gb.reset = \u6e05\u9664
+gb.pages = \u6587\u4ef6
+gb.workingCopy = \u66ab\u5b58\u8907\u672c
+gb.workingCopyWarning = \u8a72\u7248\u672c\u5eab\u4ecd\u6709\u66ab\u5b58\u8907\u672c,\u56e0\u6b64\u7121\u6cd5\u63a5\u53d7\u63a8\u9001(push)
+gb.query = \u67e5\u8a62
+gb.queryHelp = \u652f\u63f4\u6a19\u6e96\u67e5\u8a62\u8a9e\u6cd5.<p/><p/>\u8a73\u60c5\u8acb\u53c3\u8003 <a target\ = "_new" href\ = "http\://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/queryparsersyntax.html">Lucene Query Parser Syntax</a>
+gb.queryResults = \u7d50\u679c {0} - {1} ({2} \u67e5\u8a62)
+gb.noHits = \u7121\u9ede\u64ca
+gb.authored = \u6388\u6b0a
gb.committed = \u5df2\u63d0\u4ea4
-gb.committer = \u78ba\u8a8d\u63d0\u4ea4\u8005
-gb.compare = \u6bd4\u5c0d
-gb.compareToMergeBase = \u6bd4\u5c0d\u5f8c,\u5408\u4f75\u5230\u4e3b\u8981\u5de5\u4f5c\u5340
-gb.compareToN = \u8207{0}\u9032\u884c\u6bd4\u5c0d
+gb.indexedBranches = \u5206\u652f\u7d22\u5f15
+gb.indexedBranchesDescription = \u9078\u5b9a\u6b32\u57f7\u884cLucene\u7d22\u5f15\u529f\u80fd\u7684\u5206\u652f
+gb.noIndexedRepositoriesWarning = \u8ddf\u4f60\u76f8\u95dc\u7684\u7248\u672c\u5eab\u4e26\u6c92\u6709\u505aLucene\u7d22\u5f15
+gb.undefinedQueryWarning = \u672a\u8a2d\u5b9a\u67e5\u8a62\u689d\u4ef6
+gb.noSelectedRepositoriesWarning = \u8acb\u81f3\u5c11\u9078\u64c7\u4e00\u500b\u7248\u672c\u5eab
+gb.luceneDisabled = \u505c\u7528Lucene\u7d22\u5f15\u529f\u80fd
+gb.failedtoRead = \u8b80\u53d6\u5931\u6557
+gb.isNotValidFile = \u4e0d\u662f\u6709\u6548\u6a94\u6848
+gb.failedToReadMessage = Failed to read default message from {0}\!
+gb.passwordsDoNotMatch = \u5bc6\u78bc\u4e0d\u76f8\u7b26
+gb.passwordTooShort = \u5bc6\u78bc\u904e\u77ed, \u6700\u5c11{0}\u500b\u5b57\u5143
+gb.passwordChanged = \u5bc6\u78bc\u8b8a\u66f4\u6210\u529f
+gb.passwordChangeAborted = \u53d6\u6d88\u5bc6\u78bc\u8b8a\u66f4
+gb.pleaseSetRepositoryName = \u8acb\u8a2d\u5b9a\u7248\u672c\u5eab\u540d\u7a31
+gb.illegalLeadingSlash = \u7981\u6b62\u6839\u76ee\u9304(/)
+gb.illegalRelativeSlash = \u7981\u6b62\u76f8\u5c0d\u76ee\u9304(../)
+gb.illegalCharacterRepositoryName = \u7248\u672c\u5eab\u540d\u7a31\u6709\u4e0d\u5408\u6cd5\u7684\u5b57\u5143"{0}"
+gb.selectAccessRestriction = Please select access restriction\!
+gb.selectFederationStrategy = Please select federation strategy\!
+gb.pleaseSetTeamName = \u8acb\u8f38\u5165\u5718\u968a\u540d\u7a31
+gb.teamNameUnavailable = \u5718\u968a"{0}"\u4e0d\u5b58\u5728.
+gb.teamMustSpecifyRepository = \u5718\u968a\u6700\u5c11\u8981\u6307\u5b9a\u4e00\u500b\u7248\u672c\u5eab
+gb.teamCreated = \u5718\u968a"{0}"\u65b0\u589e\u6210\u529f.
+gb.pleaseSetUsername = \u8acb\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31
+gb.usernameUnavailable = \u4f7f\u7528\u8005\u540d\u7a31"{0}"\u4e0d\u53ef\u7528
+gb.combinedMd5Rename = Gitblit\u4f7f\u7528md5\u65b9\u5f0f\u5c07\u5bc6\u78bc\u7de8\u78bc(\u7121\u6cd5\u9084\u539f).\u4f60\u5fc5\u9808\u8f38\u5165\u65b0\u5bc6\u78bc.
+gb.userCreated = \u6210\u529f\u5efa\u7acb\u65b0\u4f7f\u7528\u8005"{0}"
+gb.couldNotFindFederationRegistration = \u627e\u4e0d\u5230federation registration!
+gb.failedToFindGravatarProfile = \u7121\u6cd5\u627e\u5230\u5e33\u865f{0}\u7684Gravator\u8cc7\u6599
+gb.branchStats = \u9019\u500b\u5206\u652f{2}\u6709{0}\u500b\u63d0\u4ea4\u4ee5\u53ca{1}\u500b\u6a19\u7c64
+gb.repositoryNotSpecified = \u672a\u6307\u5b9a\u7248\u672c\u5eab!
+gb.repositoryNotSpecifiedFor = \u7248\u672c\u5eab\u4e26\u6c92\u6709\u6307\u5b9a\u7d66 {0}\!
+gb.canNotLoadRepository = \u7121\u6cd5\u8f09\u5165\u7248\u672c\u5eab
+gb.commitIsNull = \u63d0\u4ea4\u5167\u5bb9\u662f\u7a7a\u7684
+gb.unauthorizedAccessForRepository = \u7248\u672c\u5eab\u672a\u6388\u6b0a\u5b58\u53d6
+gb.failedToFindCommit = \u5728{1}\u4e2d\u7121\u6cd5\u627e\u5230\u8a72 {0} \u63d0\u4ea4!
+gb.couldNotFindFederationProposal = \u641c\u5c0b\u4e0d\u5230federation proposal!
+gb.invalidUsernameOrPassword = \u932f\u8aa4\u7684\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc!
+gb.OneProposalToReview = \u6709\u4e00\u500bfederation proposal \u7b49\u5f85\u5be9\u67e5
+gb.nFederationProposalsToReview = \u7e3d\u5171\u6709{0}\u500bfederation proposals\u7b49\u5f85\u5be9\u8996
+gb.couldNotFindTag = \u627e\u4e0d\u5230\u6a19\u7c64{0}
+gb.couldNotCreateFederationProposal = \u7121\u6cd5\u5efa\u7acbfederation proposals
+gb.pleaseSetGitblitUrl = \u8acb\u8f38\u5165Gitblit URL !
+gb.pleaseSetDestinationUrl = Please enter a destination url for your proposal\!
+gb.proposalReceived = Proposal successfully received by {0}.
+gb.noGitblitFound = Sorry, {0} could not find a Gitblit instance at {1}.
+gb.noProposals = \u62b1\u6b49, {0}\u6b64\u6642\u4e26\u4e0d\u662f\u53ef\u63a5\u53d7\u7684proposals.
+gb.noFederation = Sorry, {0} is not configured to federate with any Gitblit instances.
+gb.proposalFailed = Sorry, {0} did not receive any proposal data\!
+gb.proposalError = \u62b1\u6b49, {0} \u4efd\u5831\u544a\u767c\u751f\u9810\u671f\u5916\u7684\u932f\u8aa4!
+gb.failedToSendProposal = \u63d0\u6848\u767c\u9001\u5931\u6557\!
+gb.userServiceDoesNotPermitAddUser = {0}\u4e0d\u5141\u8a31\u65b0\u589e\u4f7f\u7528\u8005\u5e33\u865f
+gb.userServiceDoesNotPermitPasswordChanges = {0}\u4e0d\u5141\u8a31\u4fee\u6539\u5bc6\u78bc
+gb.displayName = \u986f\u793a\u7684\u540d\u7a31
+gb.emailAddress = \u96fb\u5b50\u90f5\u4ef6
+gb.errorAdminLoginRequired = \u767b\u5165\u9700\u6709\u7ba1\u7406\u6b0a\u9650
+gb.errorOnlyAdminMayCreateRepository = \u53ea\u6709\u7ba1\u7406\u8005\u80fd\u5efa\u7acb\u7248\u672c\u5eab
+gb.errorOnlyAdminOrOwnerMayEditRepository = \u53ea\u6709\u7ba1\u7406\u8005\u8207\u7248\u672c\u5eab\u64c1\u6709\u8005\u80fd\u4fee\u6539\u7248\u672c\u5eab\u5c6c\u6027
+gb.errorAdministrationDisabled = \u7ba1\u7406\u6b0a\u9650\u5df2\u53d6\u6d88
+gb.lastNDays = \u6700\u8fd1{0}\u5929
gb.completeGravatarProfile = \u5b8c\u6210Gravator.com\u4e0a\u7684\u57fa\u672c\u8cc7\u6599\u8a2d\u5b9a
-gb.confirmPassword = \u78ba\u8a8d\u5bc6\u78bc
+gb.none = \u7121
+gb.line = \u884c
gb.content = \u5167\u5bb9
-gb.copyToClipboard = \u8907\u88fd\u5230\u526a\u8cbc\u677f
-gb.couldNotCreateFederationProposal = \u7121\u6cd5\u5efa\u7acb\u4e32\u9023\u7684\u5408\u4f5c\u63d0\u6848
-gb.couldNotFindFederationProposal = \u641c\u5c0b\u4e0d\u5230\u8981\u6c42\u4e32\u9023\u7684\u63d0\u6848
-gb.couldNotFindFederationRegistration = \u627e\u4e0d\u5230\u4e32\u9023\u8a3b\u518a\u55ae
-gb.couldNotFindTag = \u627e\u4e0d\u5230\u6a19\u7c64{0}
-gb.countryCode = \u570b\u5bb6\u4ee3\u78bc
-gb.create = \u5efa\u7acb
-gb.createdBy = created by
-gb.createdNewBranch = \u5efa\u7acb\u65b0\u5206\u652f
-gb.createdNewPullRequest = created pull request
-gb.createdNewTag = \u5efa\u7acb\u65b0\u6a19\u7c64
-gb.createdThisTicket = \u5df2\u958b\u7acb\u7684\u4efb\u52d9\u55ae
-gb.createFirstTicket = \u6309\u6b64\u9996\u767c\u4efb\u52d9\u55ae
-gb.createPermission = {0} (push, ref creation)
-gb.createReadme = \u5efa\u7acbREADME\u6a94\u6848
-gb.customFields = custom fields
-gb.customFieldsDescription = custom fields available to Groovy hooks
-gb.dailyActivity = \u6bcf\u65e5\u6d3b\u52d5
-gb.dashboard = \u5100\u8868\u677f
-gb.date = \u65e5\u671f
-gb.default = \u9810\u8a2d
-gb.delete = \u522a\u9664
-gb.deletedBranch = deleted branch
-gb.deletedTag = \u522a\u9664\u6a19\u7c64
-gb.deleteMilestone = \u522a\u9664\u91cc\u7a0b\u7891"{0}"?
-gb.deletePermission = {0} (push, ref creation+deletion)
+gb.empty = \u7a7a\u7684
+gb.inherited = \u7e7c\u627f
gb.deleteRepository = \u522a\u9664\u7248\u672c\u5eab"{0}"?
-gb.deleteRepositoryDescription = \u7248\u672c\u5eab\u522a\u9664\u5c07\u7121\u6cd5\u9084\u539f
-gb.deleteRepositoryHeader = \u522a\u9664\u7248\u672c\u5eab
+gb.repositoryDeleted = \u7248\u672c\u5eab"{0}"\u5df2\u522a\u9664
+gb.repositoryDeleteFailed = \u522a\u9664\u7248\u672c\u5eab"{0}"\u5931\u6557!
gb.deleteUser = \u522a\u9664\u4f7f\u7528\u8005"{0}"?
-gb.deletion = \u522a\u9664
-gb.description = \u6982\u8ff0
-gb.destinationUrl = \u50b3\u9001
-gb.destinationUrlDescription = \u50b3\u9001Gitblit\u9023\u7d50\u5230\u4f60\u7684\u5c08\u6848(proposal)
-gb.diff = \u5dee\u7570
-gb.diffCopiedFile = File was copied from {0}
-gb.diffDeletedFile = \u6a94\u6848\u5df2\u522a\u9664
-gb.diffDeletedFileSkipped = (\u522a\u9664)
-gb.diffFileDiffTooLarge = \u6a94\u6848\u592a\u5927
-gb.diffNewFile = \u6bd4\u5c0d\u65b0\u6a94\u6848
-gb.diffRenamedFile = File was renamed from {0}
-gb.diffStat = \u65b0\u589e{0}\u5217\u8207\u522a\u9664{1}\u5217
-gb.difftocurrent = \u6bd4\u5c0d\u5dee\u7570
-gb.diffTruncated = Diff truncated after the above file
-gb.disableUser = \u505c\u7528\u5e33\u6236
-gb.disableUserDescription = \u8a72\u5e33\u6236\u7121\u6cd5\u4f7f\u7528
-gb.discussion = \u8a0e\u8ad6
-gb.displayName = \u986f\u793a\u7684\u540d\u7a31
-gb.displayNameDescription = \u5e0c\u671b\u986f\u793a\u7684\u540d\u7a31
-gb.docs = \u6a94\u6848\u5340
-gb.docsWelcome1 = \u4f60\u53ef\u4ee5\u4f7f\u7528\u6a94\u6848\u5340\u5efa\u7acb\u6587\u4ef6\u5eab\u7684\u6559\u5b78\u6a94\u6848
-gb.docsWelcome2 = \u63d0\u4ea4README.md \u6216 HOME.md\u5f8c,\u518d\u958b\u59cb\u65b0\u7684\u6587\u4ef6\u5eab
-gb.doesNotExistInTree = {0}\u4e26\u6c92\u6709\u5728\u76ee\u9304{1}\u88e1\u9762
-gb.download = \u4e0b\u8f09
-gb.downloading = \u4e0b\u8f09ing
-gb.due = due
-gb.duration = \u9031\u671f
-gb.duration.days = {0}\u5929
-gb.duration.months = {0}\u6708
+gb.userDeleted = \u4f7f\u7528\u8005"{0}"\u5df2\u522a\u9664
+gb.userDeleteFailed = \u4f7f\u7528\u8005"{0}"\u522a\u9664\u5931\u6557
+gb.time.justNow = \u525b\u525b
+gb.time.today = \u4eca\u5929
+gb.time.yesterday = \u6628\u5929
+gb.time.minsAgo = {0}\u5206\u9418\u524d
+gb.time.hoursAgo = {0}\u5c0f\u6642\u524d
+gb.time.daysAgo = {0}\u5929\u524d
+gb.time.weeksAgo = {0}\u5468\u524d
+gb.time.monthsAgo = {0}\u6708\u524d
+gb.time.oneYearAgo = 1\u5e74\u524d
+gb.time.yearsAgo = {0}\u5e74\u524d
gb.duration.oneDay = 1\u5929
+gb.duration.days = {0}\u5929
gb.duration.oneMonth = 1\u6708
+gb.duration.months = {0}\u6708
gb.duration.oneYear = 1\u5e74
gb.duration.years = {0}\u5e74
-gb.edit = \u7de8\u8f2f
-gb.editMilestone = \u4fee\u6539milestone
-gb.editTicket = \u4fee\u6539\u4efb\u52d9\u55ae
-gb.editUsers = \u4fee\u6539\u5e33\u865f
-gb.effective = \u6240\u6709\u6b0a\u9650
-gb.emailAddress = \u96fb\u5b50\u90f5\u4ef6
-gb.emailAddressDescription = \u7528\u4f86\u63a5\u6536\u901a\u77e5\u7684\u4e3b\u8981\u96fb\u5b50\u90f5\u4ef6
-gb.emailCertificateBundle = \u5bc4\u767c\u7528\u6236\u7aef\u8b49\u66f8
-gb.emailMeOnMyTicketChanges = \u6211\u7684\u4efb\u52d9\u55ae\u82e5\u6709\u8b8a\u66f4,\u8acb800\u91cc\u52a0\u6025(email)\u901a\u77e5\u6211
-gb.emailMeOnMyTicketChangesDescription = \u6211\u8655\u7406\u904e\u7684\u4efb\u52d9\u55ae\u8acbemail\u901a\u77e5\u6211
-gb.empty = \u7a7a\u7684
-gb.emptyRepository = \u7a7a\u7684\u7248\u672c\u5eab
-gb.enableDocs = \u555f\u7528\u6a94\u6848\u5340
-gb.enableIncrementalPushTags = \u555f\u7528\u81ea\u52d5\u65b0\u589e\u6a19\u7c64\u529f\u80fd
-gb.enableTickets = \u555f\u7528\u4efb\u52d9\u55ae\u7cfb\u7d71
-gb.enhancementTickets = \u512a\u5316
-gb.enterKeystorePassword = \u8acb\u8f38\u5165Gitblit\u7684keystore\u5c08\u7528\u5bc6\u78bc
-gb.error = \u932f\u8aa4
-gb.errorAdministrationDisabled = \u7ba1\u7406\u6b0a\u9650\u5df2\u53d6\u6d88
-gb.errorAdminLoginRequired = \u767b\u5165\u9700\u6709\u7ba1\u7406\u6b0a\u9650
-gb.errorOnlyAdminMayCreateRepository = \u53ea\u6709\u7ba1\u7406\u8005\u80fd\u5efa\u7acb\u7248\u672c\u5eab
-gb.errorOnlyAdminOrOwnerMayEditRepository = \u53ea\u6709\u7ba1\u7406\u8005\u8207\u7248\u672c\u5eab\u64c1\u6709\u8005\u80fd\u4fee\u6539\u7248\u672c\u5eab\u5c6c\u6027
-gb.excludeFromActivity = exclude from activity page
-gb.excludeFromFederation = \u6392\u9664\u4e32\u9023
-gb.excludeFromFederationDescription = \u963b\u64cb\u5df2\u4e32\u9023\u7684Gitblit\u4f3a\u670d\u5668
-gb.excludePermission = {0} (\u6392\u9664)
-gb.exclusions = \u6392\u9664
-gb.expired = \u904e\u671f
-gb.expires = \u5230\u671f
-gb.expiring = \u5c07\u8981\u904e\u671f
-gb.export = \u532f\u51fa
-gb.extensions = \u64f4\u5145
-gb.externalPermissions = {0} access permissions are externally maintained
-gb.failedToFindAccount = \u7121\u6cd5\u641c\u5c0b\u5230\u5e33\u865f"{0}"
-gb.failedToFindCommit = Failed to find commit "{0}" in {1}\!
-gb.failedToFindGravatarProfile = \u7121\u6cd5\u627e\u5230\u5e33\u865f{0}\u7684Gravator\u8cc7\u6599
-gb.failedtoRead = \u8b80\u53d6\u5931\u6557
-gb.failedToReadMessage = Failed to read default message from {0}\!
-gb.failedToSendProposal = \u63d0\u6848\u767c\u9001\u5931\u6557\!
-gb.failedToUpdateUser = \u7121\u6cd5\u66f4\u65b0\u4f7f\u7528\u8005\u5e33\u865f
-gb.federatedRepositoryDefinitions = \u7248\u672c\u5eab\u5b9a\u7fa9
-gb.federatedSettingDefinitions = setting definitions
-gb.federatedUserDefinitions = user definitions
-gb.federateOrigin = federate the origin
-gb.federateThis = \u8207\u672c\u6587\u4ef6\u5eab\u4e32\u9023
-gb.federation = \u4e32\u9023
-gb.federationRegistration = federation registration
-gb.federationRepositoryDescription = \u8207\u5176\u4ed6gitblit\u4f3a\u670d\u5668\u5206\u4eab\u4e00\u8d77\u4f7f\u7528\u9019\u500b\u7248\u672c\u5eab
-gb.federationResults = federation pull results
-gb.federationSets = \u4e32\u9023\u7d44\u5408
-gb.federationSetsDescription = \u6b64\u6587\u4ef6\u5eab\u5c07\u5305\u542b\u65bc\u6307\u5b9a\u7684\u4e32\u9023\u7fa4\u7d44(federation sets)
-gb.federationStrategy = \u4e32\u9023\u7b56\u7565
-gb.federationStrategyDescription = \u63a7\u5236\u5982\u4f55\u5c07\u6587\u4ef6\u5eab\u8207\u5176\u4ed6Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u4e32\u9023
-gb.feed = \u8cc7\u6599\u8a02\u95b1
-gb.filesAdded = \u65b0\u589e{0}\u500b\u6a94\u6848
-gb.filesCopied = \u8907\u88fd{0}\u500b\u6a94\u6848
-gb.filesDeleted = \u522a\u9664{0}\u500b\u6a94\u6848
-gb.filesModified = \u4fee\u6539{0}\u500b\u6a94\u6848
-gb.filesRenamed = \u4fee\u6539{0}\u500b\u6a94\u6848\u540d\u7a31
-gb.filter = \u689d\u4ef6\u904e\u6ffe
-gb.filters = \u67e5\u8a62\u689d\u4ef6
-gb.findSomeRepositories = \u641c\u5c0b\u6587\u4ef6\u5eab
-gb.folder = \u76ee\u9304
+gb.authorizationControl = \u6388\u6b0a\u7ba1\u63a7
+gb.allowAuthenticatedDescription = \u6279\u51c6 RW+ \u6b0a\u9650\u7d66\u4e88\u5c08\u6848\u6210\u54e1
+gb.allowNamedDescription = grant fine-grained permissions to named users or teams
+gb.markdownFailure = \u89e3\u6790Markdown\u5931\u6557
+gb.clearCache = \u6e05\u9664\u5feb\u53d6
+gb.projects = \u7fa4\u7d44
+gb.project = \u7fa4\u7d44
+gb.allProjects = \u5168\u90e8\u7fa4\u7d44
+gb.copyToClipboard = \u8907\u88fd\u5230\u526a\u8cbc\u677f
gb.fork = \u5efa\u7acb\u5206\u652f(fork)
-gb.forkedFrom = forked from
-gb.forkInProgress = fork in progress
-gb.forkNotAuthorized = \u5f88\u62b1\u6b49, \u4f60\u7121\u5efa\u7acb\u6587\u4ef6\u5eab{0}\u5206\u652f(fork)\u7684\u6b0a\u9650
-gb.forkRepository = \u7248\u672c\u5eab{0}\u5efa\u7acb\u5206\u652f(fork)?
gb.forks = \u5206\u652f(forks)
+gb.forkRepository = \u7248\u672c\u5eab{0}\u5efa\u7acb\u5206\u652f(fork)?
+gb.repositoryForked = \u7248\u672c\u5eab{0}\u5df2\u7d93\u5efa\u7acb\u5206\u652f(fork)
+gb.repositoryForkFailed = \u5efa\u7acb\u5206\u652f(fork)\u5931\u6557
+gb.personalRepositories = \u500b\u4eba\u7248\u672c\u5eab
+gb.allowForks = \u5141\u8a31\u5efa\u7acb\u5206\u652f(forks)
+gb.allowForksDescription = \u5141\u8a31\u5df2\u6388\u6b0a\u7684\u4f7f\u7528\u8005\u5f9e\u7248\u672c\u5eab\u5efa\u7acb\u5206\u652f(fork)
+gb.forkedFrom = \u6e90\u81ea\u65bc
+gb.canFork = \u53ef\u5efa\u7acb\u5206\u652f(fork)
+gb.canForkDescription = \u53ef\u4ee5\u5efa\u7acb\u7248\u672c\u5eab\u5206\u652f(fork),\u4e26\u4e14\u8907\u88fd\u5230\u79c1\u4eba\u7248\u672c\u5eab\u4e2d
+gb.myFork = \u6aa2\u8996\u6211\u5efa\u7acb\u7684\u5206\u652f(fork)
gb.forksProhibited = \u7981\u6b62\u5efa\u7acb\u5206\u652f(forks)
-gb.forksProhibitedWarning = \u672c\u6587\u4ef6\u5eab\u7981\u6b62\u5206\u652f(fork)
-gb.free = \u91cb\u653e
-gb.frequency = \u983b\u7387
-gb.from = from
-gb.garbageCollection = \u56de\u6536\u7cfb\u7d71\u8cc7\u6e90
-gb.garbageCollectionDescription = \u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u529f\u80fd\u5c07\u6703\u6574\u9813\u9b06\u6563\u7528\u6236\u7aef\u63a8\u9001(push)\u7684\u7269\u4ef6, \u4e5f\u6703\u79fb\u9664\u6587\u4ef6\u5eab\u4e0a\u7121\u7528\u7684\u7269\u4ef6
-gb.gc = \u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u5668
+gb.forksProhibitedWarning = \u672c\u7248\u672c\u5eab\u7981\u6b62\u5206\u652f(fork)
+gb.noForks = {0}\u6c92\u6709\u5206\u652f(fork)
+gb.forkNotAuthorized = \u5f88\u62b1\u6b49, \u4f60\u7121\u5efa\u7acb\u7248\u672c\u5eab{0}\u5206\u652f(fork)\u7684\u6b0a\u9650
+gb.forkInProgress = \u6b63\u5728\u8907\u88fd\u4e2d(fork)
+gb.preparingFork = \u6b63\u5728\u6e96\u5099\u8907\u88fd\u4e2d(fork)...
+gb.isFork = \u662f\u5206\u652f\u985e\u578b(fork)
+gb.canCreate = \u53ef\u5efa\u7acb
+gb.canCreateDescription = \u80fd\u5920\u5efa\u7acb\u500b\u4eba\u7248\u672c\u5eab
+gb.illegalPersonalRepositoryLocation = \u4f60\u500b\u4eba\u7248\u672c\u5eab\u5fc5\u9808\u653e\u5728"{0}"
+gb.verifyCommitter = \u63d0\u4ea4\u8005\u9700\u9a57\u8b49
+gb.verifyCommitterDescription = \u9700\u8981\u63d0\u4ea4\u8005\u7b26\u5408\u63a8\u9001\u5e33\u865f
+gb.verifyCommitterNote = \u6240\u6709\u5408\u4f75\u52d5\u4f5c\u7686\u9808\u5f37\u5236\u4f7f\u7528"--no-ff"\u53c3\u6578
+gb.repositoryPermissions = \u7248\u672c\u5eab\u6b0a\u9650
+gb.userPermissions = \u4f7f\u7528\u8005\u6b0a\u9650
+gb.teamPermissions = \u5718\u968a\u6b0a\u9650
+gb.add = \u65b0\u589e
+gb.noPermission = \u522a\u9664\u9019\u500b\u6b0a\u9650
+gb.excludePermission = {0} \u6392\u9664(exclude)
+gb.viewPermission = {0} \u6aa2\u8996(view)
+gb.clonePermission = {0} \u8907\u88fd(clone)
+gb.pushPermission = {0} \u63a8\u9001(push)
+gb.createPermission = {0} (push, ref creation)
+gb.deletePermission = {0} (push, ref creation+deletion)
+gb.rewindPermission = {0} (push, ref creation+deletion+rewind)
+gb.permission = \u6b0a\u9650
+gb.regexPermission = \u5df2\u7d93\u4f7f\u7528\u6b63\u898f\u8868\u793a\u5f0f(regular expression)"{0}" \u8a2d\u5b9a\u6b0a\u9650\u5b8c\u7562
+gb.accessDenied = \u62d2\u7d55\u5b58\u53d6
+gb.busyCollectingGarbage = \u62b1\u6b49,Gitblit\u6b63\u5728\u56de\u6536\u7cfb\u7d71\u8cc7\u6e90\u4e2d:{0}
gb.gcPeriod = \u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u968e\u6bb5
gb.gcPeriodDescription = \u56de\u6536\u9031\u671f
gb.gcThreshold = GC \u57fa\u6578(threshold)
gb.gcThresholdDescription = \u89f8\u767c\u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u7684\u6700\u5c0f\u7269\u4ef6\u5bb9\u91cf
-gb.general = \u4e00\u822c
-gb.generalDescription = \u4e00\u822c\u8a2d\u5b9a
-gb.hasNotReviewed = \u5c1a\u672a\u6aa2\u6838\u904e
-gb.head = HEAD
-gb.headRef = \u9810\u8a2d\u5206\u652f(HEAD)
-gb.headRefDescription = \u9810\u8a2d\u5206\u652f\u5c07\u6703\u8907\u88fd\u4ee5\u53ca\u986f\u793a\u5230\u532f\u7e3d\u9801\u9762
-gb.heapAllocated = \u5df2\u4f7f\u7528\u5806\u7a4d(Heap)
-gb.heapMaximum = \u6700\u5927\u5806\u7a4d(heap)
-gb.heapUsed = \u5df2\u4f7f\u7528\u7684\u5806\u7a4d(heap)
-gb.history = \u6b77\u7a0b
-gb.home = \u9996\u9801
-gb.hookScripts = hook\u7684\u8173\u672c
-gb.hookScriptsDescription = \u7576\u63a8\u9001(push)\u81f3\u6b64Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u6642, \u57f7\u884cGroovy\u8173\u672c
+gb.ownerPermission = \u7248\u672c\u5eab\u64c1\u6709\u8005
+gb.administrator = \u7ba1\u7406\u54e1
+gb.administratorPermission = Gitblit \u7ba1\u7406\u54e1
+gb.team = \u5718\u968a
+gb.teamPermission = "{0}" \u5718\u968a\u6210\u54e1\u7684\u6b0a\u9650
+gb.missing = \u5931\u8aa4!
+gb.missingPermission = \u8a72\u6b0a\u9650\u7121\u6cd5\u5c0d\u61c9\u5230\u7248\u672c\u5eab!
+gb.mutable = \u52d5\u614b\u7d66\u4e88
+gb.specified = \u6307\u5b9a\u7d66\u4e88(\u542b\u7cfb\u7d71\u9810\u8a2d)
+gb.effective = \u6240\u6709\u6b0a\u9650
+gb.organizationalUnit = \u7d44\u7e54\u55ae\u4f4d
+gb.organization = \u7d44\u7e54
+gb.locality = \u4f4d\u7f6e
+gb.stateProvince = \u5dde\u6216\u7701
+gb.countryCode = \u570b\u5bb6\u4ee3\u78bc
+gb.properties = \u5c6c\u6027
+gb.issued = \u767c\u51fa
+gb.expires = \u5230\u671f
+gb.expired = \u904e\u671f
+gb.expiring = \u5c07\u8981\u904e\u671f
+gb.revoked = \u5df2\u64a4\u92b7
+gb.serialNumber = \u5e8f\u865f
+gb.certificates = \u8b49\u66f8
+gb.newCertificate = \u5efa\u7acb\u8b49\u66f8
+gb.revokeCertificate = \u64a4\u56de\u8b49\u66f8
+gb.sendEmail = \u767cemail
+gb.passwordHint = \u5bc6\u78bc\u63d0\u793a
+gb.ok = ok
+gb.invalidExpirationDate = \u4e0d\u6b63\u78ba\u7684\u5230\u671f\u65e5
+gb.passwordHintRequired = \u5bc6\u78bc\u63d0\u793a(\u5fc5\u8981)
+gb.viewCertificate = \u6aa2\u8996\u8b49\u66f8
+gb.subject = \u6a19\u984c
+gb.issuer = \u767c\u884c\u8005
+gb.validFrom = \u6709\u6548\u671f\u5f9e
+gb.validUntil = \u6709\u6548\u671f\u81f3
+gb.publicKey = \u516c\u958b\u91d1\u9470
+gb.signatureAlgorithm = \u7c3d\u7ae0\u6f14\u7b97\u6cd5
+gb.sha1FingerPrint = SHA-1 Fingerprint
+gb.md5FingerPrint = MD5 Fingerprint
+gb.reason = \u539f\u56e0
+gb.revokeCertificateReason = \u8acb\u8f38\u5165\u64a4\u56de\u8b49\u66f8\u7406\u7531
+gb.unspecified = \u672a\u6307\u5b9a
+gb.keyCompromise = \u91d1\u9470\u5bc6\u78bc\u5916\u6d29
+gb.caCompromise = CA compromise
+gb.affiliationChanged = affiliation changed
+gb.superseded = \u5df2\u88ab\u66ff\u4ee3
+gb.cessationOfOperation = cessation of operation
+gb.privilegeWithdrawn = \u53d6\u6d88\u6b0a\u9650
+gb.time.inMinutes = {0}\u5206\u9418\u5167
+gb.time.inHours = {0}\u5c0f\u6642\u5167
+gb.time.inDays = {0}\u5929\u5167
gb.hostname = \u4e3b\u6a5f\u540d\u7a31
gb.hostnameRequired = \u8acb\u8f38\u5165\u4e3b\u6a5f\u540d\u7a31
-gb.ignore_whitespace =\u5ffd\u7565\u7a7a\u767d
-gb.illegalCharacterRepositoryName = \u7248\u672c\u5eab\u540d\u7a31\u6709\u4e0d\u5408\u6cd5\u7684\u5b57\u5143"{0}"
-gb.illegalLeadingSlash = \u7981\u6b62\u6839\u76ee\u9304(/)
-gb.illegalPersonalRepositoryLocation = \u4f60\u79c1\u4eba\u7248\u672c\u5eab\u5fc5\u9808\u653e\u5728"{0}"
-gb.illegalRelativeSlash = \u7981\u6b62\u76f8\u5c0d\u76ee\u9304(../)
-gb.imgdiffSubtract = Subtract (black = identical)
-gb.in = in
-gb.inclusions = inclusions
-gb.incrementalPushTagMessage = \u7576[{0}]\u5206\u652f\u63a8\u9001\u5f8c,\u81ea\u52d5\u7d66\u4e88\u6a19\u7c64\u865f.
-gb.indexedBranches = \u5206\u652f\u7d22\u5f15
-gb.indexedBranchesDescription = \u9078\u5b9a\u6b32\u57f7\u884cLucene\u7d22\u5f15\u529f\u80fd\u7684\u5206\u652f
-gb.inherited = \u7e7c\u627f
-gb.initialCommit = \u521d\u6b21\u63d0\u4ea4
-gb.initialCommitDescription = \u4ee5\u4e0b\u6b65\u9a5f\u5c07\u6703\u8b93\u4f60\u99ac\u4e0a\u57f7\u884c<code>git clone</code>.\u5982\u679c\u4f60\u672c\u6a5f\u5df2\u6709\u6b64\u6587\u4ef6\u5eab\u4e14\u57f7\u884c\u904e<code>git init</code>,\u8acb\u8df3\u904e\u6b64\u6b65\u9a5f.
-gb.initWithGitignore = \u5305\u542b .gitignore \u6a94\u6848
-gb.initWithGitignoreDescription = \u65b0\u589e\u4e00\u500b\u8a2d\u5b9a\u6a94\u7528\u4f86\u6307\u5b9a\u54ea\u4e9b\u6a94\u6848\u6216\u76ee\u9304\u9700\u8981\u5ffd\u7565
-gb.initWithReadme = \u5305\u542bREADME\u6587\u4ef6
-gb.initWithReadmeDescription = \u6587\u4ef6\u5eab\u5c07\u7522\u751f\u7c21\u55aeREADME\u6587\u4ef6
-gb.invalidExpirationDate = \u4e0d\u6b63\u78ba\u7684\u5230\u671f\u65e5
-gb.invalidUsernameOrPassword = \u932f\u8aa4\u7684\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc!
-gb.isFederated = \u5df2\u7d93\u4e32\u9023
-gb.isFork = \u662f\u5206\u652f\u985e\u578b(fork)
-gb.isFrozen = \u51cd\u7d50\u63a5\u6536
-gb.isFrozenDescription = \u7981\u6b62\u63a8\u9001(push)
-gb.isMirror = \u8a72\u6587\u4ef6\u5eab\u70ba\u93e1\u50cf(mirror)
-gb.isNotValidFile = \u4e0d\u662f\u6b63\u5e38\u6a94\u6848
-gb.isSparkleshared = \u8a72\u6587\u4ef6\u5eab\u5df2\u70baSparkleshared (http://sparkleshare.org)
-gb.issued = \u767c\u51fa
-gb.issuer = issuer
+gb.newSSLCertificate = \u65b0\u7684\u4f3a\u670d\u5668SSL\u8b49\u66f8
+gb.newCertificateDefaults = \u65b0\u8b49\u66f8\u9810\u8a2d\u503c
+gb.duration = \u9031\u671f
+gb.certificateRevoked = \u8b49\u66f8{0,number,0} \u5df2\u7d93\u88ab\u53d6\u6d88
+gb.clientCertificateGenerated = \u6210\u529f\u7522\u751f{0}\u7684\u65b0\u8b49\u66f8
+gb.sslCertificateGenerated = \u6210\u529f\u7522\u751f\u7d66{0}\u7684\u670d\u5668SSL\u8b49\u66f8
+gb.newClientCertificateMessage = \u6ce8\u610f:\n'password'\u5bc6\u78bc\u4e26\u4e0d\u662f\u4f7f\u7528\u8005\u5bc6\u78bc, \u800c\u662f\u7528\u4f86\u4fdd\u8b77\u4f7f\u7528\u8005\u500b\u4eba\u7684keystore.\u8a72\u5bc6\u78bc\u4e26\u4e0d\u6703\u5132\u5b58, \u56e0\u6b64\u5fc5\u9808\u8a2d\u5b9a\u63d0\u793a(hint), \u8a72\u63d0\u793a\u5c07\u6703\u5beb\u5728\u4f7f\u7528\u8005\u7684README\u6587\u4ef6\u88e1\u9762.
+gb.certificate = \u8b49\u66f8
+gb.emailCertificateBundle = \u5bc4\u767c\u7528\u6236\u7aef\u8b49\u66f8
+gb.pleaseGenerateClientCertificate = \u8acb\u7522\u751f\u7d66{0}\u4f7f\u7528\u7684\u7528\u6236\u7aef\u8b49\u66f8
+gb.clientCertificateBundleSent = {0}\u7684\u7528\u6236\u8b49\u66f8\u5df2\u5bc4\u767c
+gb.enterKeystorePassword = \u8acb\u8f38\u5165Gitblit\u7684keystore\u5c08\u7528\u5bc6\u78bc
+gb.warning = \u8b66\u544a
gb.jceWarning = Your Java Runtime Environment does not have the "JCE Unlimited Strength Jurisdiction Policy" files.\nThis will limit the length of passwords you may use to encrypt your keystores to 7 characters.\nThese policy files are an optional download from Oracle.\n\nWould you like to continue and generate the certificate infrastructure anyway?\n\nAnswering No will direct your browser to Oracle's download page so that you may download the policy files.
-gb.key = \u91d1\u9470
-gb.keyCompromise = \u91d1\u9470\u5bc6\u78bc\u5916\u6d29
-gb.labels = \u6a19\u8a18
-gb.languagePreference = \u5e38\u7528\u8a9e\u8a00
-gb.languagePreferenceDescription = \u9078\u64c7\u4f60\u60f3\u8981\u7684Gitblit\u7ffb\u8b6f
-gb.lastChange = \u6700\u8fd1\u4fee\u6539
-gb.lastLogin = \u6700\u8fd1\u767b\u5165
-gb.lastNDays = \u6700\u8fd1{0}\u5929
-gb.lastPull = \u4e0a\u6b21\u4e0b\u8f09(pull)
-gb.leaveComment = \u7559\u4e0b\u8a3b\u89e3
-gb.line = \u884c
-gb.loading = \u8f09\u5165
-gb.local = \u672c\u5730\u7aef
-gb.locality = \u4f4d\u7f6e
-gb.log = \u65e5\u8a8c
-gb.login = \u767b\u5165
-gb.logout = \u767b\u51fa
-gb.looksGood = \u770b\u8d77\u4f86\u5f88\u597d
-gb.luceneDisabled = \u505c\u7528Lucene\u7d22\u5f15\u529f\u80fd
-gb.mailingLists = \u90f5\u4ef6\u540d\u55ae
-gb.maintenanceTickets = \u7dad\u8b77
-gb.manage = \u7ba1\u7406
-gb.manual = \u81ea\u884c\u8f38\u5165
-gb.markdown = markdown
-gb.markdownFailure = \u89e3\u6790Markdown\u5931\u6557
gb.maxActivityCommits = \u6700\u5927\u63d0\u4ea4\u6d3b\u8e8d\u7387
gb.maxActivityCommitsDescription = \u6700\u5927\u63d0\u4ea4\u6d3b\u8e8d\u6578\u91cf
-gb.maxHits = \u6700\u5927\u9ede\u64ca
-gb.md5FingerPrint = MD5 Fingerprint
-gb.mentions = \u63d0\u5230
-gb.mentionsMeTickets = \u63d0\u5230\u4f60
-gb.merge = \u5408\u4f75
-gb.mergeBase = \u57fa\u672c\u5408\u4f75
-gb.merged = \u5df2\u5408\u4f75
-gb.mergedPatchset = \u5c07\u88dc\u4e01\u5408\u4f75
-gb.mergedPullRequest = \u5408\u4f75\u63a8\u9001\u8981\u6c42
-gb.mergeSha = mergeSha
-gb.mergeStep1 = Check out a new branch to review the changes \u2014 run this from your project directory
-gb.mergeStep2 = Bring in the proposed changes and review
-gb.mergeStep3 = \u5c07\u63d0\u6848\u4fee\u6539\u5167\u5bb9\u5408\u4f75\u5230\u4f3a\u670d\u5668\u4e0a
-gb.mergeTo = \u5408\u4f75\u5230
-gb.mergeToDescription = \u9810\u8a2d\u5c07\u6587\u4ef6\u76f8\u95dc\u88dc\u4e01\u5305\u8207\u6307\u5b9a\u5206\u652f(branch)\u5408\u4f75
-gb.mergingViaCommandLine = \u7d93\u7531\u6307\u4ee4\u57f7\u884c\u5408\u4f75
-gb.mergingViaCommandLineNote = \u5982\u679c\u4f60\u4e0d\u60f3\u8981\u4f7f\u7528\u81ea\u52d5\u5408\u4f75\u529f\u80fd,\u6216\u662f\u6309\u4e0b\u5408\u4f75\u6309\u9215, \u4f60\u53ef\u4ee5\u4e0b\u6307\u4ee4\u624b\u52d5\u5408\u4f75
-gb.message = \u8a0a\u606f
-gb.metricAuthorExclusions = \u91cf\u5316\u7d71\u8a08\u6642\u6392\u9664\u6d3b\u8e8d\u5e33\u6236
-gb.metrics = \u91cf\u5316\u7d71\u8a08
-gb.milestone = \u91cc\u7a0b\u7891
-gb.milestoneDeleteFailed = \u522a\u9664\u91cc\u7a0b\u7891"{0}"\u5931\u6557
-gb.milestoneProgress = {0}\u958b\u555f,{1}\u7d50\u675f
-gb.milestones = \u91cc\u7a0b\u7891
-gb.mirrorOf = {0}\u7684\u93e1\u50cf
-gb.mirrorWarning = \u8a72\u6587\u4ef6\u5eab\u5c6c\u65bc\u93e1\u50cf, \u4e0d\u80fd\u5920\u63a5\u6536\u63a8\u9001(push)
-gb.miscellaneous = \u5176\u4ed6
-gb.missing = \u5931\u8aa4!
-gb.missingIntegrationBranchMore = \u76ee\u6a19\u5206\u652f\u4e0d\u5728\u6b64\u7248\u672c\u5eab
-gb.missingPermission = the repository for this permission is missing\!
-gb.missingUsername = \u7f3a\u5c11\u4f7f\u7528\u8005\u540d\u7a31
-gb.modification = \u4fee\u6539
+gb.noMaximum = \u7121\u6700\u5927\u503c
+gb.attributes = \u5c6c\u6027
+gb.serveCertificate = \u555f\u7528\u4f7f\u7528\u6b64\u8b49\u66f8\u7684https\u529f\u80fd
+gb.sslCertificateGeneratedRestart = \u6210\u529f\u7522\u751f\u7d66{0}\u4f7f\u7528\u7684SSL\u8b49\u66f8\n\u4f60\u5fc5\u9808\u91cd\u65b0\u555f\u52d5Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u624d\u80fd\u555f\u7528\u65b0\u7684\u8b49\u66f8\n\nf you are launching with the '--alias' parameter you will have to set that to ''--alias {0}''.
+gb.validity = validity
+gb.siteName = \u7db2\u7ad9\u540d\u7a31
+gb.siteNameDescription = \u4f3a\u670d\u5668\u7c21\u7a31
+gb.excludeFromActivity = exclude from activity page
+gb.isSparkleshared = \u8a72\u7248\u672c\u5eab\u5df2\u70baSparkleshared (http://sparkleshare.org)
+gb.owners = \u64c1\u6709\u8005
+gb.sessionEnded = session\u5df2\u7d93\u53d6\u6d88
+gb.closeBrowser = \u8acb\u95dc\u9589\u700f\u89bd\u5668\u7d50\u675f\u6b64\u767b\u5165\u968e\u6bb5
+gb.doesNotExistInTree = {0}\u4e26\u6c92\u6709\u5728\u76ee\u9304{1}\u88e1\u9762
+gb.enableIncrementalPushTags = \u555f\u7528\u81ea\u52d5\u65b0\u589e\u6a19\u7c64\u529f\u80fd
+gb.useIncrementalPushTagsDescription = \u63a8\u9001\u6642\u5c07\u81ea\u52d5\u65b0\u589e\u6a19\u7c64\u865f\u78bc
+gb.incrementalPushTagMessage = \u7576[{0}]\u5206\u652f\u63a8\u9001\u5f8c,\u81ea\u52d5\u7d66\u4e88\u6a19\u7c64\u865f.
+gb.externalPermissions = {0} access permissions are externally maintained
+gb.viewAccess = \u4f60\u6c92\u6709Gitblit\u8b80\u53d6\u6216\u662f\u4fee\u6539\u6b0a\u9650
+gb.overview = \u6982\u89c0
+gb.dashboard = \u5100\u8868\u677f
gb.monthlyActivity = \u6708\u6d3b\u52d5
-gb.moreChanges = \u6240\u6709\u8b8a\u66f4...
-gb.moreHistory = \u66f4\u591a\u6b77\u53f2\u7d00\u9304...
-gb.moreLogs = \u66f4\u591a\u63d0\u4ea4 ...
-gb.mutable = \u52d5\u614b\u7d66\u4e88
-gb.myDashboard = \u6211\u7684\u5100\u8868\u677f
-gb.myFork = \u6aa2\u8996\u6211\u5efa\u7acb\u7684\u5206\u652f(fork)
gb.myProfile = \u6211\u7684\u57fa\u672c\u8cc7\u6599
-gb.myRepositories = \u6211\u7684\u7248\u672c\u5eab
-gb.myTickets = \u6211\u7684\u4efb\u52d9\u55ae
-gb.myUrlDescription = \u4f60Gitblit\u4f3a\u670d\u5668\u7684\u516c\u958bURL
-gb.name = \u540d\u5b57
-gb.nameDescription = \u4f7f\u7528"/"\u505a\u70ba\u6587\u4ef6\u5eab\u7fa4\u7d44\u5206\u985e. \u5982: library/mycoolib.git
-gb.namedPushPolicy = Restrict Push (Named)
-gb.namedPushPolicyDescription = \u4efb\u4f55\u4eba\u7686\u53ef\u6aa2\u8996\u8207\u8907\u88fd(clone)\u6587\u4ef6\u5eab. \u4f60\u53ef\u53e6\u5916\u6307\u5b9a\u8ab0\u80fd\u5920\u6709\u63a8\u9001\u529f\u80fd(push)
-gb.nAttachments = {0}\u500b\u9644\u4ef6
-gb.nClosedTickets = {0}\u9805\u7d50\u675f
-gb.nComments = {0}\u500b\u8a3b\u89e3
-gb.nCommits = {0}\u4efd\u63d0\u4ea4
-gb.needsImprovement = \u9700\u8981\u512a\u5316
-gb.new = \u5efa\u7acb
-gb.newCertificate = \u5efa\u7acb\u8b49\u66f8
-gb.newCertificateDefaults = \u65b0\u8b49\u66f8\u9810\u8a2d\u503c
-gb.newClientCertificateMessage = \u6ce8\u610f:\n'password'\u5bc6\u78bc\u4e26\u4e0d\u662f\u4f7f\u7528\u8005\u5bc6\u78bc, \u800c\u662f\u7528\u4f86\u4fdd\u8b77\u4f7f\u7528\u8005\u500b\u4eba\u7684keystore.\u8a72\u5bc6\u78bc\u4e26\u4e0d\u6703\u5132\u5b58, \u56e0\u6b64\u5fc5\u9808\u8a2d\u5b9a\u63d0\u793a(hint), \u8a72\u63d0\u793a\u5c07\u6703\u5beb\u5728\u4f7f\u7528\u8005\u7684README\u6587\u4ef6\u88e1\u9762.
-gb.newMilestone = \u5efa\u7acb\u91cc\u7a0b\u7891
-gb.newRepository = \u5efa\u7acb\u7248\u672c\u5eab
-gb.newSSLCertificate = \u65b0\u7684\u4f3a\u670d\u5668SSL\u8b49\u66f8
-gb.newTeam = \u5efa\u7acb\u5718\u968a
-gb.newTicket = \u65b0\u589e\u4efb\u52d9\u55ae
-gb.newUser = \u5efa\u7acb\u4f7f\u7528\u8005
-gb.nextPull = next pull
-gb.nFederationProposalsToReview = \u7e3d\u5171\u6709{0}\u500b\u4e32\u9023\u8a08\u756b\u7b49\u5f85\u5be9\u8996
+gb.compare = \u6bd4\u5c0d
+gb.manual = \u81ea\u884c\u8f38\u5165
+gb.from = \u5f9e
+gb.to = \u81f3
+gb.at = at
+gb.of = \u5c08\u6848\u70ba
+gb.in = in
+gb.moreChanges = \u6240\u6709\u8b8a\u66f4...
+gb.pushedNCommitsTo = {0}\u500b\u63d0\u4ea4\u5df2\u63a8\u9001\u81f3
+gb.pushedOneCommitTo = 1\u500b\u63d0\u4ea4\u5df2\u63a8\u9001\u81f3
+gb.commitsTo = {0} \u4efd\u63d0\u4ea4\u81f3\u5206\u652f
+gb.oneCommitTo = 1\u500b\u63d0\u4ea4\u81f3\u5206\u652f
+gb.byNAuthors = \u7d93\u7531{0}\u500b\u4f5c\u8005
+gb.byOneAuthor = \u7d93\u7531{0}
+gb.viewComparison = \u6bd4\u8f03\u9019{0}\u500b\u63d0\u4ea4 \u00bb
gb.nMoreCommits = \u9084\u6709{0}\u4efd\u63d0\u4ea4 \u00bb
+gb.oneMoreCommit = \u9084\u6709\u4e00\u500b\u63d0\u4ea4 \u00bb
+gb.pushedNewTag = \u65b0\u6a19\u7c64\u5df2\u63a8\u9001(pushed)
+gb.createdNewTag = \u5efa\u7acb\u65b0\u6a19\u7c64
+gb.deletedTag = \u522a\u9664\u6a19\u7c64
+gb.pushedNewBranch = \u5df2\u63a8\u9001(pushed)\u4e4b\u5206\u652f -
+gb.createdNewBranch = \u5efa\u7acb\u65b0\u5206\u652f
+gb.deletedBranch = \u5df2\u522a\u9664\u7684\u5206\u652f
+gb.createdNewPullRequest = \u5efa\u7acb pull request
+gb.mergedPullRequest = \u5408\u4f75\u63a8\u9001\u8981\u6c42
+gb.rewind = REWIND
+gb.star = \u91cd\u8981
+gb.unstar = \u53d6\u6d88
+gb.stargazers = stargazers
+gb.starredRepositories = \u91cd\u8981\u7684\u7248\u672c\u5eab
+gb.failedToUpdateUser = \u7121\u6cd5\u66f4\u65b0\u4f7f\u7528\u8005\u5e33\u865f
+gb.myRepositories = \u6211\u7684\u7248\u672c\u5eab
gb.noActivity = \u904e\u53bb{0}\u5929\u4f86,\u4e26\u6c92\u6709\u6d3b\u52d5\u7d00\u9304
+gb.findSomeRepositories = \u641c\u5c0b\u7248\u672c\u5eab
+gb.metricAuthorExclusions = \u7d71\u8a08\u6642\u6392\u9664\u6d3b\u8e8d\u5e33\u6236
+gb.myDashboard = \u5100\u8868\u677f
+gb.failedToFindAccount = \u7121\u6cd5\u641c\u5c0b\u5230\u5e33\u865f"{0}"
+gb.reflog = \u76f8\u95dc\u65e5\u8a8c
+gb.active = \u6d3b\u52d5
+gb.starred = \u91cd\u8981
+gb.owned = \u64c1\u6709
+gb.starredAndOwned = \u91cd\u8981 & \u64c1\u6709
+gb.reviewPatchset = {0}\u500breview {1}\u500bpatchset
+gb.todaysActivityStats = \u4eca\u5929/\u6709{2}\u500b\u4f5c\u8005\u5b8c\u6210{1}\u500b\u63d0\u4ea4
+gb.todaysActivityNone = \u4eca\u5929/\u7121
gb.noActivityToday = \u4eca\u5929\u6c92\u6709\u6d3b\u52d5\u7d00\u9304
-gb.noComments = \u6c92\u6709\u5099\u8a3b
+gb.anonymousUser = \u533f\u540d
+gb.commitMessageRenderer = \u63d0\u4ea4\u8a0a\u606f\u5448\u73fe\u65b9\u5f0f
+gb.diffStat = \u65b0\u589e{0}\u5217\u8207\u522a\u9664{1}\u5217
+gb.home = \u9996\u9801
+gb.isMirror = \u8a72\u7248\u672c\u5eab\u70ba\u93e1\u50cf(mirror)
+gb.mirrorOf = {0}\u7684\u93e1\u50cf
+gb.mirrorWarning = \u8a72\u7248\u672c\u5eab\u5c6c\u65bc\u93e1\u50cf, \u4e0d\u80fd\u5920\u63a5\u6536\u63a8\u9001(push)
+gb.docsWelcome1 = \u4f60\u53ef\u4ee5\u4f7f\u7528\u6a94\u6848\u5340\u5efa\u7acb\u7248\u672c\u5eab\u7684\u6559\u5b78\u6a94\u6848
+gb.docsWelcome2 = \u63d0\u4ea4README.md \u6216 HOME.md\u5f8c,\u518d\u958b\u59cb\u65b0\u7684\u7248\u672c\u5eab
+gb.createReadme = \u5efa\u7acbREADME\u6a94\u6848
+gb.responsible = \u8ca0\u8cac\u4eba\u54e1
+gb.createdThisTicket = \u5df2\u958b\u7acb\u7684\u4efb\u52d9
+gb.proposedThisChange = proposed this change
+gb.uploadedPatchsetN = \u88dc\u4e01{0}\u5df2\u4e0a\u50b3
+gb.uploadedPatchsetNRevisionN = \u88dc\u4e01{0}\u4fee\u6539\u7248\u672c{1}\u5df2\u4e0a\u50b3
+gb.mergedPatchset = \u5c07\u88dc\u4e01\u5408\u4f75
+gb.commented = \u5df2\u8a3b\u89e3
gb.noDescriptionGiven = \u6c92\u6709\u7d66\u4e88\u7c21\u8ff0
-gb.noFederation = Sorry, {0} is not configured to federate with any Gitblit instances.
-gb.noForks = {0}\u6c92\u6709\u5206\u652f(fork)
-gb.noGitblitFound = Sorry, {0} could not find a Gitblit instance at {1}.
-gb.noHits = \u7121\u9ede\u64ca
-gb.noIndexedRepositoriesWarning = \u8ddf\u4f60\u76f8\u95dc\u7684\u6587\u4ef6\u5eab\u4e26\u6c92\u6709\u505aLucene\u7d22\u5f15
-gb.noMaximum = \u7121\u6700\u5927\u503c
-gb.noMilestoneSelected = \u672a\u9078\u53d6\u91cc\u7a0b\u7891
-gb.none = \u7121
-gb.nOpenTickets = {0}\u9805\u958b\u555f\u4e2d
-gb.noPermission = \u522a\u9664\u9019\u500b\u6b0a\u9650
-gb.noProposals = \u62b1\u6b49, {0}\u6b64\u6642\u4e26\u4e0d\u662f\u53ef\u63a5\u53d7\u7684\u8a08\u756b
-gb.noSelectedRepositoriesWarning = \u8acb\u81f3\u5c11\u9078\u64c7\u4e00\u500b\u6587\u4ef6\u5eab
-gb.notifyChangedOpenTickets = \u5df2\u958b\u555f\u7684\u4efb\u52d9\u55ae\u6709\u7570\u52d5\u8acb\u767c\u9001\u901a\u77e5
-gb.notRestricted = \u533f\u540d\u72c0\u614b\u53ef\u4ee5View, Clone\u8207Push
-gb.notSpecified = \u7121\u6307\u5b9a
+gb.toBranch = \u5230\u5206\u652f {0}
+gb.createdBy = \u5efa\u7acb\u8005
+gb.oneParticipant = {0}\u53c3\u8207
gb.nParticipants = {0}\u500b\u53c3\u8207
-gb.nTotalTickets = \u7e3d\u5171{0}\u9805
-gb.object = \u7269\u4ef6
-gb.of = \u7684
-gb.ok = ok
-gb.oneAttachment = {0}\u500b\u9644\u4ef6
+gb.noComments = \u6c92\u6709\u5099\u8a3b
gb.oneComment = {0}\u500b\u8a3b\u89e3
-gb.oneCommit = 1\u500b\u63d0\u4ea4
-gb.oneCommitTo = 1\u500b\u63d0\u4ea4\u5230
-gb.oneMoreCommit = \u9084\u6709\u4e00\u500b\u63d0\u4ea4 \u00bb
-gb.oneParticipant = {0}\u53c3\u8207
-gb.OneProposalToReview = \u6709\u4e00\u500b\u4e32\u9023\u7684\u63d0\u6848\u7b49\u5f85\u5be9\u67e5
-gb.opacityAdjust = Adjust opacity
+gb.nComments = {0}\u500b\u8a3b\u89e3
+gb.oneAttachment = {0}\u500b\u9644\u4ef6
+gb.nAttachments = {0}\u500b\u9644\u4ef6
+gb.milestone = milestone
+gb.compareToMergeBase = \u6bd4\u5c0d\u5f8c,\u5408\u4f75\u5230\u4e3b\u8981\u5de5\u4f5c\u5340
+gb.compareToN = \u8207{0}\u9032\u884c\u6bd4\u5c0d
gb.open = \u958b\u555f
-gb.openMilestones = \u6253\u958b\u91cc\u7a0b\u7891
-gb.organization = \u7d44\u7e54
-gb.organizationalUnit = \u7d44\u7e54\u55ae\u4f4d
-gb.origin = origin
-gb.originDescription = \u6b64\u6587\u4ef6\u5eabURL\u5df2\u7d93\u88ab\u8907\u88fd(cloned)\u4e86
-gb.overdue = \u904e\u671f
-gb.overview = \u6982\u89c0
-gb.owned = \u64c1\u6709\u7684
-gb.owner = \u64c1\u6709\u8005
-gb.ownerDescription = \u64c1\u6709\u8005\u53ef\u4fee\u6539\u6587\u4ef6\u5eab\u8a2d\u5b9a\u503c
-gb.ownerPermission = \u6587\u4ef6\u5eab\u6240\u6709\u8005
-gb.owners = \u6240\u6709\u8005
-gb.ownersDescription = \u6240\u6709\u8005\u53ef\u4ee5\u7ba1\u7406\u6587\u4ef6\u5eab,\u4f46\u662f\u4e0d\u5141\u8a31\u4fee\u6539\u540d\u7a31(\u79c1\u4eba\u6587\u4ef6\u5eab\u4f8b\u5916)
-gb.pageFirst = \u7b2c\u4e00\u7b46
-gb.pageNext = \u4e0b\u4e00\u9801
-gb.pagePrevious = \u4e0a\u4e00\u9801
-gb.pages = \u6587\u4ef6
-gb.parent = \u4e0a\u500b\u7248\u672c
-gb.password = \u5bc6\u78bc
-gb.passwordChangeAborted = \u53d6\u6d88\u5bc6\u78bc\u8b8a\u66f4
-gb.passwordChanged = \u5bc6\u78bc\u8b8a\u66f4\u6210\u529f
-gb.passwordHint = \u5bc6\u78bc\u63d0\u793a
-gb.passwordHintRequired = \u5bc6\u78bc\u63d0\u793a(\u5fc5\u8981)
-gb.passwordsDoNotMatch = \u5bc6\u78bc\u4e0d\u76f8\u7b26
-gb.passwordTooShort = \u5bc6\u78bc\u904e\u77ed, \u6700\u5c11{0}\u500b\u5b57\u5143
-gb.patch = \u4fee\u88dc\u6a94
-gb.patchset = \u88dc\u4e01
-gb.patchsetAlreadyMerged = \u8a72\u88dc\u4e01\u5df2\u7d93\u5408\u4f75\u5230{0}
+gb.closed = \u95dc\u9589
+gb.merged = \u5df2\u5408\u4f75
+gb.ticketPatchset = {0}\u4efb\u52d9\u55ae,{1}\u88dc\u4e01
gb.patchsetMergeable = \u8a72\u88dc\u4e01\u53ef\u4ee5\u81ea\u52d5\u8207{0}\u5408\u4f75
gb.patchsetMergeableMore = \u4f7f\u7528\u547d\u4ee4\u529f\u80fd,\u8b93\u6b64\u88dc\u4e01\u53ef\u4ee5\u8207{0}\u5408\u4f75
-gb.patchsetN = \u88dc\u4e01{0}
-gb.patchsetNotApproved = \u8a72\u88dc\u4e01\u7248\u672c\u4e26\u6c92\u6709\u88ab\u6279\u51c6\u8207{0}\u5408\u4f75
-gb.patchsetNotApprovedMore = \u8a72\u88dc\u4e01\u5fc5\u9808\u7531\u5be9\u67e5\u8005\u6279\u51c6
+gb.patchsetAlreadyMerged = \u8a72\u88dc\u4e01\u5df2\u7d93\u5408\u4f75\u5230{0}
gb.patchsetNotMergeable = \u8a72\u88dc\u4e01\u4e0d\u80fd\u81ea\u52d5\u8207{0}\u5408\u4f75
gb.patchsetNotMergeableMore = \u5fc5\u9808\u4ee5rebased\u6216\u662f\u624b\u52d5\u8207{0}\u5408\u4f75\u7684\u65b9\u5f0f\u624d\u80fd\u89e3\u6c7a\u8a72\u88dc\u4e01\u9020\u6210\u7684\u885d\u7a81
-gb.patchsetVetoedMore = \u5be9\u8996\u8005\u5df2\u7d93\u5c0d\u6b64\u88dc\u4e01\u6295\u7968
-gb.permission = \u6b0a\u9650
-gb.permissions = \u6b0a\u9650
-gb.permittedTeams = permitted teams
-gb.permittedUsers = permitted users
-gb.personalRepositories = \u500b\u4eba\u6587\u4ef6\u5eab
-gb.pleaseGenerateClientCertificate = \u8acb\u7522\u751f\u7d66{0}\u4f7f\u7528\u7684\u7528\u6236\u7aef\u8b49\u66f8
-gb.pleaseSelectGitIgnore = \u8acb\u9078\u64c7\u4e00\u500b.gitignore\u6a94\u6848
-gb.pleaseSelectProject = \u8acb\u9078\u64c7\u5c08\u6848!
-gb.pleaseSetDestinationUrl = Please enter a destination url for your proposal\!
-gb.pleaseSetGitblitUrl = \u8acb\u8f38\u5165Gitblit URL !
-gb.pleaseSetRepositoryName = \u8acb\u8a2d\u5b9a\u7248\u672c\u5eab\u540d\u7a31
-gb.pleaseSetTeamName = \u8acb\u8f38\u5165\u5718\u968a\u540d\u7a31
-gb.pleaseSetUsername = \u8acb\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31
-gb.plugins = \u63d2\u4ef6
-gb.postReceiveDescription = \u63a5\u5230\u63d0\u4ea4\u7533\u8acb\u5f8c,<em>\u4e26\u4e14\u5728refs\u5b8c\u7562\u5f8c</em>, \u5c07\u6703\u57f7\u884cPost-receive hook..<p>This is the appropriate hook for notifications, build triggers, etc.</p>
-gb.postReceiveScripts = post-receive\u8173\u672c
-gb.preferences = \u9810\u8a2d\u5e38\u7528\u503c
-gb.preparingFork = \u6b63\u5728\u6e96\u5099\u8907\u88fd\u4e2d(fork)...
-gb.preReceiveDescription = \u63a5\u5230\u63d0\u4ea4\u7533\u8acb\u5f8c,<em>\u4f46\u5728\u9084\u6c92\u6709\u66f4\u65b0refs\u524d</em>, \u5c07\u6703\u57f7\u884cPre-receive hook. <p>This is the appropriate hook for rejecting a push.</p>
-gb.preReceiveScripts = pre-receive \u8173\u672c
+gb.patchsetNotApproved = \u8a72\u88dc\u4e01\u7248\u672c\u4e26\u6c92\u6709\u88ab\u6279\u51c6\u8207{0}\u5408\u4f75
+gb.patchsetNotApprovedMore = \u8a72\u88dc\u4e01\u5fc5\u9808\u7531\u5be9\u67e5\u8005\u6279\u51c6
+gb.patchsetVetoedMore = \u5be9\u67e5\u8005\u5df2\u7d93\u5c0d\u6b64\u88dc\u4e01\u6295\u7968
+gb.write = \u8f38\u5165
+gb.comment = \u8a3b\u89e3
gb.preview = \u9810\u89bd
-gb.priority = \u512a\u5148
-gb.privilegeWithdrawn = \u53d6\u6d88\u6b0a\u9650
-gb.project = \u7fa4\u7d44
-gb.projects = \u7fa4\u7d44
-gb.properties = \u5c6c\u6027
-gb.proposal = \u63d0\u6848
-gb.proposalError = \u62b1\u6b49, {0} \u4efd\u5831\u544a\u767c\u751f\u9810\u671f\u5916\u7684\u932f\u8aa4!
-gb.proposalFailed = Sorry, {0} did not receive any proposal data\!
-gb.proposalReceived = Proposal successfully received by {0}.
-gb.proposals = \u8981\u6c42\u806f\u5408\u7684\u63d0\u6848
+gb.leaveComment = \u7559\u4e0b\u8a3b\u89e3
+gb.showHideDetails = \u986f\u793a/\u96b1\u85cf \u8a73\u89e3\u5167\u5bb9
+gb.acceptNewPatchsets = \u5141\u8a31\u88dc\u4e01
+gb.acceptNewPatchsetsDescription = \u63a5\u53d7\u5230\u7248\u672c\u5eab\u9032\u884c\u4fee\u88dc\u52d5\u4f5c
+gb.acceptNewTickets = \u5141\u8a31\u5efa\u7acb\u4efb\u52d9
+gb.acceptNewTicketsDescription = \u5141\u8a31\u65b0\u589e"\u81ed\u87f2","\u512a\u5316","\u4efb\u52d9"\u5404\u985e\u578b\u4efb\u52d9
+gb.requireApproval = \u9700\u6279\u51c6
+gb.requireApprovalDescription = \u5408\u4f75\u6309\u9215\u555f\u7528\u524d,\u88dc\u4e01\u5305\u5fc5\u9808\u5148\u6279\u51c6
+gb.topic = \u8a71\u984c
gb.proposalTickets = \u63d0\u6848\u4fee\u6539
-gb.proposedThisChange = proposed this change
-gb.proposeInstructions = To start, craft a patchset and upload it with Git. Gitblit will link your patchset to this ticket by the id.
-gb.proposePatchset = \u63d0\u51fa\u88dc\u4e01
-gb.proposePatchsetNote = \u6b61\u8fce\u5c0d\u6b64\u4efb\u52d9\u55ae\u63d0\u4f9b\u88dc\u4e01
-gb.proposeWith = propose a patchset with {0}
-gb.ptCheckout = Fetch & checkout the current patchset to a review branch
-gb.ptDescription = the Gitblit patchset tool
-gb.ptDescription1 = Barnum is a command-line companion for Git that simplifies the syntax for working with Gitblit Tickets and Patchsets.
-gb.ptDescription2 = Barnum requires Python 3 and native Git. It runs on Windows, Linux, and Mac OS X.
-gb.ptMerge = \u53d6\u5f97\u76ee\u524d\u88dc\u4e01,\u7136\u5f8c\u8207\u4f60\u672c\u6a5f\u7aef\u7684\u5206\u652f\u5408\u4f75
-gb.ptSimplifiedCollaboration = simplified collaboration syntax
-gb.ptSimplifiedMerge = simplified merge syntax
-gb.publicKey = \u516c\u958b\u91d1\u9470
-gb.pushedNCommitsTo = {0}\u500b\u63d0\u4ea4\u5df2\u63a8\u9001\u81f3
-gb.pushedNewBranch = \u65b0\u5206\u652f\u5df2\u63a8\u9001(pushed)
-gb.pushedNewTag = \u65b0\u6a19\u7c64\u5df2\u63a8\u9001(pushed)
-gb.pushedOneCommitTo = 1\u500b\u63d0\u4ea4\u5df2\u63a8\u9001\u81f3
-gb.pushPermission = {0}(\u63a8\u9001)
-gb.pushRestricted = authenticated push
-gb.queries = \u67e5\u8a62\u7d50\u679c
-gb.query = \u67e5\u8a62
-gb.queryHelp = \u652f\u63f4\u6a19\u6e96\u67e5\u8a62\u8a9e\u6cd5.<p/><p/>\u8a73\u60c5\u8acb\u53c3\u8003 <a target\ = "_new" href\ = "http\://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/queryparsersyntax.html">Lucene Query Parser Syntax</a>
-gb.queryResults = results {0} - {1} ({2} hits)
+gb.bugTickets = \u81ed\u87f2
+gb.enhancementTickets = \u512a\u5316
+gb.taskTickets = \u4efb\u52d9
gb.questionTickets = \u63d0\u554f
-gb.raw = \u539f\u59cb
-gb.reason = \u539f\u56e0
-gb.receive = \u63a5\u6536
-gb.received = \u5df2\u63a5\u6536
-gb.receiveSettings = \u8a2d\u5b9a\u63a5\u6536\u65b9\u5f0f
-gb.receiveSettingsDescription = \u63a7\u7ba1\u63a8\u9001\u5230\u6587\u4ef6\u5eab\u7684\u63a5\u6536\u65b9\u5f0f
-gb.recent = \u6700\u8fd1
-gb.recentActivity = \u6700\u8fd1\u6d3b\u8e8d\u72c0\u6cc1
-gb.recentActivityNone = \u904e\u53bb{0}\u5929/\u7121
-gb.recentActivityStats = \u904e\u53bb{0}\u5929,\u4e00\u5171\u6709{2}\u4eba\u57f7\u884c{1}\u4efd\u63d0\u4ea4
-gb.reflog = \u76f8\u95dc\u65e5\u8a8c
-gb.refresh = \u5237\u65b0
-gb.refs = \u5f15\u7528
-gb.regexPermission = \u5df2\u7d93\u4f7f\u7528\u6b63\u898f\u8868\u793a\u5f0f(regular expression)"{0}" \u8a2d\u5b9a\u6b0a\u9650\u5b8c\u7562
-gb.registration = \u8a3b\u518a
-gb.registrations = federation registrations
-gb.releaseDate = \u767c\u8868\u65e5
-gb.remote = \u9060\u7aef
-gb.removeVote = \u79fb\u9664\u6295\u7968
-gb.rename = \u6539\u540d\u7a31
-gb.repositories = \u6587\u4ef6\u5eab
-gb.repository = \u7248\u672c\u5eab
-gb.repositoryDeleted = \u7248\u672c\u5eab"{0}"\u5df2\u522a\u9664
-gb.repositoryDeleteFailed = \u522a\u9664\u7248\u672c\u5eab"{0}"\u5931\u6557!
-gb.repositoryDoesNotAcceptPatchsets = \u8a72\u7248\u672c\u5eab\u4e0d\u63a5\u53d7\u88dc\u4e01
-gb.repositoryForked = \u7248\u672c\u5eab{0}\u5df2\u7d93\u5efa\u7acb\u5206\u652f(fork)
-gb.repositoryForkFailed= \u5efa\u7acb\u5206\u652f(fork)\u5931\u6557
-gb.repositoryIsFrozen = \u8a72\u7248\u672c\u5eab\u5df2\u51cd\u7d50
-gb.repositoryIsMirror = \u8a72\u7248\u672c\u5eab\u70ba\u552f\u8b80\u8907\u672c
-gb.repositoryNotSpecified = \u672a\u6307\u5b9a\u7248\u672c\u5eab!
-gb.repositoryNotSpecifiedFor = \u7248\u672c\u5eab\u4e26\u6c92\u6709\u6307\u5b9a\u7d66 {0}\!
-gb.repositoryPermissions = \u7248\u672c\u5eab\u6b0a\u9650
-gb.repositoryUrl = \u7248\u672c\u5eab url
gb.requestTickets = \u512a\u5316 & \u4efb\u52d9
-gb.requireApproval = \u9700\u6279\u51c6
-gb.requireApprovalDescription = \u5408\u4f75\u6309\u9215\u555f\u7528\u524d,\u88dc\u4e01\u5305\u5fc5\u9808\u5148\u6279\u51c6
-gb.reset = \u6e05\u9664
-gb.responsible = \u8ca0\u8cac\u4eba\u54e1
-gb.restrictedRepositories = restricted repositories
-gb.review = \u8907\u67e5(review)
-gb.reviewedPatchsetRev = reviewed patchset {0} revision {1}\: {2}
-gb.reviewers = \u5be9\u67e5\u8005
-gb.reviewPatchset = review {0} patchset {1}
-gb.reviews = reviews
-gb.revisionHistory = \u4fee\u6539\u7d00\u9304
-gb.revokeCertificate = \u64a4\u56de\u8b49\u66f8
-gb.revokeCertificateReason = \u8acb\u8f38\u5165\u64a4\u56de\u8b49\u66f8\u7406\u7531
-gb.revoked = \u5df2\u64a4\u92b7
-gb.rewind = REWIND
-gb.rewindPermission = {0} (push, ref creation+deletion+rewind)
-gb.save = \u5132\u5b58
-gb.search = \u641c\u5c0b
-gb.searchForAuthor = Search for commits authored by
-gb.searchForCommitter = Search for commits committed by
-gb.searchTickets = \u641c\u5c0b\u4efb\u52d9\u55ae
-gb.searchTicketsTooltip = \u627e\u5230{0}\u4efd\u4efb\u52d9\u55ae
-gb.searchTooltip = \u641c\u5c0b{0}
-gb.searchTypeTooltip = \u9078\u64c7\u641c\u5c0b\u985e\u578b
-gb.selectAccessRestriction = Please select access restriction\!
-gb.selected = \u9078\u5b9a
-gb.selectFederationStrategy = Please select federation strategy\!
-gb.sendEmail = \u767cemail
-gb.sendProposal = \u63d0\u6848
-gb.serialNumber = \u5e8f\u865f
-gb.serveCertificate = \u555f\u7528\u4f7f\u7528\u6b64\u8b49\u66f8\u7684https\u529f\u80fd
-gb.serverDoesNotAcceptPatchsets = \u672c\u4f3a\u670d\u5668\u4e0d\u63a5\u53d7\u88dc\u4e01
-gb.servers = \u4f3a\u670d\u5668
-gb.servletContainer = servlet\u5bb9\u5668
-gb.sessionEnded = session\u5df2\u7d93\u53d6\u6d88
-gb.setDefault = \u8a2d\u70ba\u9810\u8a2d\u503c
-gb.settings = \u8a2d\u5b9a
-gb.severity = \u91cd\u8981
-gb.sha1FingerPrint = SHA-1 Fingerprint
-gb.show_whitespace = \u986f\u793a\u7a7a\u767d
-gb.showHideDetails = \u986f\u793a/\u96b1\u85cf \u8a73\u89e3\u5167\u5bb9
-gb.showReadme = \u986f\u793areadme\u6587\u4ef6
-gb.showReadmeDescription = \u5728\u532f\u7e3d\u9801\u9762\u4e2d\u986f\u793a"readme"(markdown\u683c\u5f0f)
-gb.showRemoteBranches = \u986f\u793a\u9060\u7aef\u5206\u652f
-gb.showRemoteBranchesDescription = \u986f\u793a\u9060\u7aef\u5206\u652f(branches)
-gb.signatureAlgorithm = \u7c3d\u7ae0\u6f14\u7b97\u6cd5
-gb.since = \u5f9e
-gb.siteName = \u7ad9\u53f0\u540d\u7a31
-gb.siteNameDescription = \u4f3a\u670d\u5668\u7c21\u7a31
-gb.size = \u5bb9\u91cf
-gb.skipSizeCalculation = \u7565\u904e\u5bb9\u91cf\u8a08\u7b97
-gb.skipSizeCalculationDescription = \u4e0d\u8a08\u7b97\u6587\u4ef6\u5eab\u5bb9\u91cf(\u52a0\u5feb\u7db2\u9801\u8f09\u5165\u901f\u5ea6)
-gb.skipSummaryMetrics = \u7565\u904e\u91cf\u5316\u532f\u7e3d
-gb.skipSummaryMetricsDescription = \u4e0d\u8981\u8a08\u7b97\u91cf\u5316\u4e26\u4e14\u986f\u793a\u5728\u532f\u7e3d\u9801\u9762\u4e0a(\u52a0\u5feb\u901f\u5ea6)
+gb.yourCreatedTickets = \u4f60\u65b0\u589e\u7684
+gb.yourWatchedTickets = \u4f60\u76e3\u770b\u7684
+gb.mentionsMeTickets = \u8207\u4f60\u76f8\u95dc
+gb.updatedBy = \u66f4\u65b0\u8005
gb.sort = \u6392\u5e8f
-gb.sortHighestPriority = \u6700\u9ad8\u512a\u5148
-gb.sortHighestSeverity = \u6700\u91cd\u8981
-gb.sortLeastComments = \u6700\u5c11\u5099\u8a3b
-gb.sortLeastPatchsetRevisions = \u6700\u5c11\u88dc\u4e01\u4fee\u6539
+gb.sortNewest = \u6700\u65b0
+gb.sortOldest = \u6700\u820a
+gb.sortMostRecentlyUpdated = \u6700\u8fd1\u66f4\u65b0
gb.sortLeastRecentlyUpdated = \u6700\u8fd1\u6700\u5c11\u8b8a\u52d5
-gb.sortLeastVotes = \u6700\u5c11\u6295\u7968
-gb.sortLowestPriority = \u6700\u4f4e\u512a\u5148
-gb.sortLowestSeverity = \u6700\u4e0d\u91cd\u8981
gb.sortMostComments = \u6700\u591a\u5099\u8a3b
+gb.sortLeastComments = \u6700\u5c11\u5099\u8a3b
gb.sortMostPatchsetRevisions = \u6700\u591a\u88dc\u4e01\u4fee\u6b63
-gb.sortMostRecentlyUpdated = \u6700\u8fd1\u66f4\u65b0
+gb.sortLeastPatchsetRevisions = \u6700\u5c11\u88dc\u4e01\u4fee\u6539
gb.sortMostVotes = \u6700\u591a\u6295\u7968
-gb.sortNewest = \u6700\u65b0
-gb.sortOldest = \u6700\u820a
-gb.specified = \u6307\u5b9a\u7d66\u4e88(\u542b\u7cfb\u7d71\u9810\u8a2d)
-gb.sshKeyCommentDescription = \u8acb\u8f38\u5165\u5099\u8a3b, \u82e5\u7121\u5099\u8a3b, \u5c07\u81ea\u8a02\u586b\u5165key data
-gb.sshKeyPermissionDescription = \u6307\u5b9a\u8a72SSH key\u6240\u64c1\u6709\u7684\u5b58\u53d6\u6b0a\u9650
-gb.sshKeys = SSH Keys
-gb.sshKeysDescription = SSH \u516c\u958b\u91d1\u9470\u662f\u5bc6\u78bc\u8a8d\u8b49\u5916\u66f4\u5b89\u5168\u7684\u9078\u9805
-gb.sslCertificateGenerated = \u6210\u529f\u7522\u751f\u7d66{0}\u7684\u670d\u5668SSL\u8b49\u66f8
-gb.sslCertificateGeneratedRestart = \u6210\u529f\u7522\u751f\u7d66{0}\u4f7f\u7528\u7684SSL\u8b49\u66f8\n\u4f60\u5fc5\u9808\u91cd\u65b0\u555f\u52d5Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u624d\u80fd\u555f\u7528\u65b0\u7684\u8b49\u66f8\n\nf you are launching with the '--alias' parameter you will have to set that to ''--alias {0}''.
-gb.star = \u91cd\u8981
-gb.stargazers = stargazers
-gb.starred = \u91cd\u8981
-gb.starredAndOwned = \u91cd\u8981\u7684 & \u64c1\u6709\u7684
-gb.starredRepositories = \u91cd\u8981\u7684\u6587\u4ef6\u5eab
-gb.starting = \u555f\u52d5\u4e2d
-gb.stateProvince = \u5dde\u6216\u7701
-gb.stats = \u7d71\u8a08
-gb.status = \u72c0\u614b
-gb.stepN = \u6b65\u9a5f{0}
-gb.stopWatching = \u505c\u6b62\u8ffd\u8e64(watching)
-gb.subject = \u6a19\u984c
-gb.subscribe = \u8a02\u95b1
-gb.summary = \u532f\u7e3d
-gb.superseded = \u5df2\u88ab\u66ff\u4ee3
-gb.tag = \u6a19\u7c64
-gb.tagger = tagger
-gb.tags = \u6a19\u7c64
-gb.taskTickets = \u4efb\u52d9
-gb.team = \u5718\u968a
-gb.teamCreated = \u5718\u968a"{0}"\u65b0\u589e\u6210\u529f.
-gb.teamMembers = \u5718\u968a\u6210\u54e1
-gb.teamMemberships = \u5718\u968a\u6210\u54e1(memberships)
-gb.teamMustSpecifyRepository = \u5718\u968a\u6700\u5c11\u8981\u6307\u5b9a\u4e00\u500b\u7248\u672c\u5eab
-gb.teamName = \u5718\u968a\u540d\u7a31
-gb.teamNameUnavailable = \u5718\u968a"{0}"\u4e0d\u5b58\u5728.
-gb.teamPermission = "{0}" \u5718\u968a\u6210\u54e1\u7684\u6b0a\u9650
-gb.teamPermissions = \u5718\u968a\u6b0a\u9650
-gb.teamPermissionsDescription = \u4f60\u53ef\u4ee5\u6307\u5b9a\u5718\u968a\u6b0a\u9650.\u9019\u4e9b\u8a2d\u5b9a\u5c07\u6703\u53d6\u4ee3\u539f\u672c\u5718\u968a\u9810\u8a2d\u6b0a\u9650
-gb.teams = \u53c3\u8207\u7684\u5718\u968a
-gb.ticket = \u4efb\u52d9\u55ae
-gb.ticketAssigned = \u5df2\u6307\u5b9a
-gb.ticketComments = \u8a3b\u89e3
-gb.ticketId = \u4efb\u52d9\u55aeID
-gb.ticketIsClosed = \u8a72\u4efb\u52d9\u55ae\u5df2\u7d93\u7d50\u6848
-gb.ticketN = \u4efb\u52d9\u55ae\u865f#{0}
-gb.ticketOpenDate = \u767c\u884c\u65e5
-gb.ticketPatchset = {0}\u4efb\u52d9\u55ae,{1}\u88dc\u4e01
-gb.tickets = \u4efb\u52d9\u55ae
-gb.ticketSettings = \u4efb\u52d9\u55ae\u5167\u5bb9\u8a2d\u5b9a
-gb.ticketStatus = \u72c0\u614b
-gb.ticketsWelcome = \u4f60\u53ef\u4ee5\u5229\u7528\u4efb\u52d9\u55ae\u7cfb\u7d71\u5efa\u69cb\u51fa\u5f85\u8fa6\u4e8b\u9805, \u81ed\u87f2\u56de\u5831\u5340\u4ee5\u53ca\u88dc\u4e01\u5305\u7684\u5354\u540c\u5408\u4f5c
-gb.time.daysAgo = {0}\u5929\u524d
-gb.time.hoursAgo = {0}\u5c0f\u6642\u524d
-gb.time.inDays = {0}\u5929\u5167
-gb.time.inHours = {0}\u5c0f\u6642\u5167
-gb.time.inMinutes = {0}\u5206\u9418\u5167
-gb.time.justNow = \u525b\u525b
-gb.time.minsAgo = {0}\u5206\u9418\u524d
-gb.time.monthsAgo = {0}\u6708\u524d
-gb.time.oneYearAgo = 1\u5e74\u524d
-gb.time.today = \u4eca\u5929
-gb.time.weeksAgo = {0}\u5468\u524d
-gb.time.yearsAgo = {0}\u5e74\u524d
-gb.time.yesterday = \u6628\u5929
-gb.title = \u6a19\u984c
-gb.to = to
-gb.toBranch = to {0}
-gb.todaysActivityNone = \u4eca\u5929/\u7121
-gb.todaysActivityStats = \u4eca\u5929/\u6709{2}\u500b\u4f5c\u8005\u5b8c\u6210{1}\u500b\u63d0\u4ea4
-gb.token = token
-gb.tokenAllDescription = \u6240\u6709\u7248\u672c\u5eab,\u4f7f\u7528\u8005\u8207\u8a2d\u5b9a
-gb.tokenJurDescription = \u6240\u6709\u7248\u672c\u5eab
-gb.tokens = federation tokens
-gb.tokenUnrDescription = \u6240\u6709\u7248\u672c\u5eab\u8207\u4f7f\u7528\u8005
-gb.topic = \u8a71\u984c
+gb.sortLeastVotes = \u6700\u5c11\u6295\u7968
gb.topicsAndLabels = \u8a71\u984c\u8207\u6a19\u8a18
-gb.transportPreference = \u9810\u8a2d\u901a\u8a0a\u5354\u5b9a
-gb.transportPreferenceDescription = \u8a2d\u5b9a\u4f60\u5e38\u7528\u7684\u9023\u7dda\u901a\u8a0a\u5354\u5b9a\u4ee5\u7528\u4f86\u8907\u88fd(clone)
-gb.tree = \u76ee\u9304
-gb.type = \u985e\u578b
-gb.unauthorizedAccessForRepository = \u7248\u672c\u5eab\u672a\u6388\u6b0a\u5b58\u53d6
-gb.undefinedQueryWarning = \u672a\u8a2d\u5b9a\u67e5\u8a62\u689d\u4ef6
-gb.unspecified = \u672a\u6307\u5b9a
-gb.unstar = \u53d6\u6d88
+gb.milestones = milestones
+gb.noMilestoneSelected = \u672a\u9078\u53d6milestone
+gb.notSpecified = \u7121\u6307\u5b9a
+gb.due = \u622a\u6b62
+gb.queries = \u67e5\u8a62\u7d50\u679c
+gb.searchTicketsTooltip = \u627e\u5230{0}\u4efd\u4efb\u52d9
+gb.searchTickets = \u641c\u5c0b\u4efb\u52d9
+gb.new = \u5efa\u7acb
+gb.newTicket = \u65b0\u589e\u4efb\u52d9
+gb.editTicket = \u4fee\u6539\u4efb\u52d9
+gb.ticketsWelcome = \u4f60\u53ef\u4ee5\u5229\u7528\u4efb\u52d9\u7cfb\u7d71\u5efa\u69cb\u51fa\u5f85\u8fa6\u4e8b\u9805, \u81ed\u87f2\u56de\u5831\u5340\u4ee5\u53ca\u88dc\u4e01\u5305\u7684\u5354\u540c\u5408\u4f5c
+gb.createFirstTicket = \u6309\u6b64\u5efa\u7acb\u7b2c\u4e00\u500b\u4efb\u52d9
+gb.title = \u6a19\u984c
+gb.changedStatus = changed the status
+gb.discussion = \u8a0e\u8ad6
gb.updated = \u5df2\u66f4\u65b0
-gb.updatedBy = updated by
-gb.uploadedPatchsetN = \u88dc\u4e01{0}\u5df2\u4e0a\u50b3
-gb.uploadedPatchsetNRevisionN = \u88dc\u4e01{0}\u4fee\u6539\u7248\u672c{1}\u5df2\u4e0a\u50b3
-gb.url = URL
-gb.useDocsDescription = \u8a08\u7b97\u6587\u4ef6\u5eab\u88e1\u9762\u7684Markdown\u6a94\u6848
-gb.useIncrementalPushTagsDescription = \u63a8\u9001\u6642\u5c07\u81ea\u52d5\u65b0\u589e\u6a19\u7c64\u865f\u78bc
-gb.userCreated = \u6210\u529f\u5efa\u7acb\u65b0\u4f7f\u7528\u8005"{0}"
-gb.userDeleted = \u4f7f\u7528\u8005"{0}"\u5df2\u522a\u9664
-gb.userDeleteFailed = \u4f7f\u7528\u8005"{0}"\u522a\u9664\u5931\u6557
-gb.username = \u4f7f\u7528\u8005\u540d\u7a31
-gb.usernameUnavailable = \u4f7f\u7528\u8005\u540d\u7a31"{0}"\u4e0d\u53ef\u7528
-gb.userPermissions = \u4f7f\u7528\u8005\u6b0a\u9650
-gb.userPermissionsDescription = \u4f60\u53ef\u4ee5\u91dd\u5c0d\u5e33\u865f\u8a2d\u5b9a\u6b0a\u9650(\u9019\u4e9b\u8a2d\u5b9a\u5c07\u8986\u84cb\u5718\u968a\u6216\u5176\u4ed6\u6b0a\u9650)
-gb.users = \u4f7f\u7528\u8005
-gb.userServiceDoesNotPermitAddUser = {0}\u4e0d\u5141\u8a31\u65b0\u589e\u4f7f\u7528\u8005\u5e33\u865f
-gb.userServiceDoesNotPermitPasswordChanges = {0}\u4e0d\u5141\u8a31\u4fee\u6539\u5bc6\u78bc
-gb.useTicketsDescription = readonly, distributed Ticgit issues
-gb.validFrom = valid from
-gb.validity = validity
-gb.validUntil = valid until
-gb.verifyCommitter = \u63d0\u4ea4\u8005\u9700\u9a57\u8b49
-gb.verifyCommitterDescription = \u9700\u8981\u63d0\u4ea4\u8005\u7b26\u5408\u63a8\u9001\u5e33\u865f
-gb.verifyCommitterNote = \u6240\u6709\u5408\u4f75\u52d5\u4f5c\u7686\u9808\u5f37\u5236\u4f7f\u7528"--no-ff"\u53c3\u6578
-gb.version = \u7248\u672c
-gb.veto = veto
-gb.view = \u6aa2\u8996
-gb.viewAccess = \u4f60\u6c92\u6709Gitblit\u8b80\u53d6\u6216\u662f\u4fee\u6539\u6b0a\u9650
-gb.viewCertificate = \u6aa2\u8996\u8b49\u66f8
-gb.viewComparison = \u6bd4\u8f03\u9019{0}\u500b\u63d0\u4ea4 \u00bb
-gb.viewPermission = {0} (\u6aa2\u8996)
-gb.viewPolicy = Restrict View, Clone, & Push
-gb.viewPolicyDescription = \u9078\u64c7\u53ef\u4ee5\u5728\u6587\u4ef6\u5eab\u6aa2\u8996,\u8907\u88fd(clone)\u8207\u63a8\u9001(push)\u7684\u4f7f\u7528\u8005, \u9664\u6b64\u4e4b\u5916\u5176\u4ed6\u4eba\u7686\u7121\u6b0a\u9650
-gb.viewRestricted = authenticated view, clone, & push
+gb.proposePatchset = \u63d0\u51fa\u88dc\u4e01
+gb.proposePatchsetNote = \u6b61\u8fce\u5c0d\u6b64\u4efb\u52d9\u63d0\u4f9b\u88dc\u4e01
+gb.proposeInstructions = \u9996\u5148, \u5efa\u7acb\u88dc\u4e01\u4e26\u4e14\u540c\u6b65\u5230\u6b64gitblit\u4f3a\u670d\u5668. Gitblit \u8b93\u88dc\u4e01\u8207\u672c\u6b21\u4efb\u52d9ID(ticket ID)\u9023\u7d50.
+gb.proposeWith = \u5982\u4f55\u5728{0} \u4e0a\u5efa\u7acb\u88dc\u4e01
+gb.revisionHistory = \u4fee\u6539\u7d00\u9304
+gb.merge = \u5408\u4f75
+gb.action = \u52d5\u4f5c
+gb.patchset = \u88dc\u4e01
+gb.all = \u5168\u90e8
+gb.mergeBase = \u57fa\u672c\u5408\u4f75
+gb.checkout = \u6aa2\u51fa(checkout)
+gb.checkoutViaCommandLine = \u4f7f\u7528\u6307\u4ee4Checkout
+gb.checkoutViaCommandLineNote = \u4f60\u53ef\u4ee5\u5f9e\u4f60\u7248\u672c\u5eab\u4e2dcheckout\u4e00\u4efd,\u7136\u5f8c\u9032\u884c\u6e2c\u8a66
+gb.checkoutStep1 = Fetch the current patchset \u2014 run this from your project directory
+gb.checkoutStep2 = \u5c07\u8a72\u88dc\u4e01\u8f49\u51fa\u5230\u65b0\u7684\u5206\u652f\u5f8c\u7528\u4f86\u6aa2\u8996
+gb.mergingViaCommandLine = \u7d93\u7531\u6307\u4ee4\u57f7\u884c\u5408\u4f75
+gb.mergingViaCommandLineNote = \u5982\u679c\u4f60\u4e0d\u60f3\u8981\u4f7f\u7528\u81ea\u52d5\u5408\u4f75\u529f\u80fd,\u6216\u662f\u6309\u4e0b\u5408\u4f75\u6309\u9215, \u4f60\u53ef\u4ee5\u4e0b\u6307\u4ee4\u624b\u52d5\u5408\u4f75
+gb.mergeStep1 = Check out a new branch to review the changes \u2014 run this from your project directory
+gb.mergeStep2 = Bring in the proposed changes and review
+gb.mergeStep3 = \u5c07\u63d0\u6848\u4fee\u6539\u5167\u5bb9\u66f4\u65b0\u5230\u4f3a\u670d\u5668\u4e0a
+gb.download = \u4e0b\u8f09
+gb.ptDescription = Gitblit \u88dc\u4e01\u5de5\u5177
+gb.ptCheckout = Fetch & checkout the current patchset to a review branch
+gb.ptMerge = \u53d6\u5f97\u76ee\u524dpatchset,\u7136\u5f8c\u8207\u4f60\u672c\u6a5f\u7aef\u7684\u5206\u652f\u5408\u4f75
+gb.ptDescription1 = Barnum is a command-line companion for Git that simplifies the syntax for working with Gitblit Tickets and Patchsets.
+gb.ptSimplifiedCollaboration = simplified collaboration syntax
+gb.ptSimplifiedMerge = simplified merge syntax
+gb.ptDescription2 = Barnum requires Python 3 and native Git. It runs on Windows, Linux, and Mac OS X.
+gb.stepN = \u6b65\u9a5f{0}
+gb.watchers = \u76e3\u7763\u8005
+gb.votes = \u6295\u7968
gb.vote = \u5c0d{0}\u6295\u7968
-gb.voters = votes
-gb.votes = votes
-gb.warning = \u8b66\u544a
gb.watch = \u76e3\u770b{0}
-gb.watchers = \u76e3\u770b\u8005
+gb.removeVote = \u79fb\u9664\u6295\u7968
+gb.stopWatching = \u505c\u6b62\u8ffd\u8e64(watching)
gb.watching = \u76e3\u770b\u4e2d
-gb.workingCopy = \u5de5\u4f5c\u8907\u672c
-gb.workingCopyWarning = \u8a72\u6587\u4ef6\u5eab\u4ecd\u6709\u5de5\u4f5c\u8907\u672c,\u56e0\u6b64\u7121\u6cd5\u63a5\u53d7\u63a8\u9001(push)
-gb.write = write
-gb.youDoNotHaveClonePermission = \u4f60\u4e0d\u5141\u8a31\u8907\u88fd(clone)\u6b64\u6587\u4ef6\u5eab
+gb.comments = \u8a3b\u89e3
+gb.addComment = \u65b0\u589e\u8a3b\u89e3
+gb.export = \u532f\u51fa
+gb.oneCommit = 1\u500b\u63d0\u4ea4
+gb.nCommits = {0}\u4efd\u63d0\u4ea4
+gb.addedOneCommit = \u63d0\u4ea41\u500b\u6a94\u6848
+gb.addedNCommits = {0}\u500b\u6a94\u6848\u63d0\u4ea4\u5b8c\u7562
+gb.commitsInPatchsetN = \u88dc\u4e01 {0} \u7684\u63d0\u4ea4
+gb.patchsetN = \u88dc\u4e01{0}
+gb.reviewedPatchsetRev = reviewed patchset {0} revision {1}\: {2}
+gb.review = \u6aa2\u67e5(review)
+gb.reviews = \u6aa2\u67e5(reviews)
+gb.veto = \u5426\u6c7a
+gb.needsImprovement = \u9700\u8981\u512a\u5316
+gb.looksGood = \u770b\u8d77\u4f86\u5f88\u597d
+gb.approve = \u901a\u904e
+gb.hasNotReviewed = \u5c1a\u672a\u6aa2\u6838\u904e
+gb.about = \u95dc\u65bc
+gb.ticketN = \u4efb\u52d9\u7de8\u865f#{0}
+gb.disableUser = \u505c\u7528\u5e33\u6236
+gb.disableUserDescription = \u8a72\u5e33\u6236\u7121\u6cd5\u4f7f\u7528
+gb.any = \u4efb\u4f55
+gb.milestoneProgress = {0}\u958b\u555f,{1}\u7d50\u675f
+gb.nOpenTickets = {0}\u9805\u958b\u555f\u4e2d
+gb.nClosedTickets = {0}\u9805\u7d50\u675f
+gb.nTotalTickets = \u7e3d\u5171{0}\u9805
+gb.body = \u5167\u5bb9
+gb.mergeSha = mergeSha
+gb.mergeTo = \u5408\u4f75\u5230
+gb.labels = \u6a19\u8a18
+gb.reviewers = \u5be9\u67e5\u8005
+gb.voters = votes
+gb.mentions = \u63d0\u5230
+gb.canNotProposePatchset = \u4e0d\u80fd\u63d0\u4f9b\u88dc\u4e01
+gb.repositoryIsMirror = \u8a72\u7248\u672c\u5eab\u70ba\u552f\u8b80\u8907\u672c
+gb.repositoryIsFrozen = \u8a72\u7248\u672c\u5eab\u5df2\u51cd\u7d50
+gb.repositoryDoesNotAcceptPatchsets = \u8a72\u7248\u672c\u5eab\u4e0d\u63a5\u53d7\u88dc\u4e01
+gb.serverDoesNotAcceptPatchsets = \u672c\u4f3a\u670d\u5668\u4e0d\u63a5\u53d7\u88dc\u4e01
+gb.ticketIsClosed = \u8a72\u4efb\u52d9\u5df2\u7d93\u7d50\u6848
+gb.mergeToDescription = \u9810\u8a2d\u5c07\u6587\u4ef6\u76f8\u95dc\u88dc\u4e01\u5305\u8207\u6307\u5b9a\u5206\u652f(branch)\u5408\u4f75
+gb.anonymousCanNotPropose = \u533f\u540d\u8005\u4e0d\u80fd\u63d0\u4f9b\u88dc\u4e01
+gb.youDoNotHaveClonePermission = \u4f60\u4e0d\u5141\u8a31\u8907\u88fd(clone)\u6b64\u7248\u672c\u5eab
+gb.myTickets = \u6211\u7684\u4efb\u52d9
gb.yourAssignedTickets = \u6307\u6d3e\u7d66\u4f60\u7684
-gb.yourCreatedTickets = \u7531\u4f60\u65b0\u589e\u7684
-gb.yourWatchedTickets = \u4f60\u60f3\u770b\u7684
-gb.zip = zip\u58d3\u7e2e\u6a94
-gb.ticketState =
-gb.repositoryForkFailed =
-gb.anonymousUser =
-gb.oneAttachment =
-gb.viewPolicy =
-gb.emailMeOnMyTicketChangesDescription =
+gb.newMilestone = \u5efa\u7acbmilestone
+gb.editMilestone = \u4fee\u6539milestone
+gb.deleteMilestone = \u522a\u9664milestone"{0}"?
+gb.milestoneDeleteFailed = \u522a\u9664milestone"{0}"\u5931\u6557
+gb.notifyChangedOpenTickets = \u5df2\u958b\u555f\u7684\u4efb\u52d9\u6709\u7570\u52d5\u8acb\u767c\u9001\u901a\u77e5
+gb.overdue = \u904e\u671f
+gb.openMilestones = \u5df2\u958b\u555f\u7684 milestones
+gb.closedMilestones = \u5df2\u95dc\u9589\u7684 milestones
+gb.administration = \u7ba1\u7406\u6b0a\u9650
+gb.plugins = \u5957\u4ef6
+gb.extensions = \u64f4\u5145
+gb.pleaseSelectProject = \u8acb\u9078\u64c7\u5c08\u6848!
+gb.accessPolicy = \u5b58\u53d6\u653f\u7b56
+gb.accessPolicyDescription = \u9078\u64c7\u7528\u4f86\u63a7\u5236\u7248\u672c\u5eab\u7684\u5b58\u53d6\u653f\u7b56\u4ee5\u53ca\u6b0a\u9650\u8a2d\u5b9a
+gb.anonymousPolicy = \u533f\u540d\u72c0\u614b\u53ef\u4ee5\u6aa2\u8996(view),\u8907\u88fd(clone)\u8207\u63a8\u9001(push)
+gb.anonymousPolicyDescription = \u6240\u6709\u4eba\u7686\u53ef\u6aa2\u8996(view),\u8907\u88fd(clone)\u8207\u63a8\u9001(push)\u6587\u4ef6\u5230\u7248\u672c\u5eab
+gb.authenticatedPushPolicy = \u9650\u5236\u63a8\u9001(Push)(\u6388\u6b0a)
+gb.authenticatedPushPolicyDescription = \u6240\u6709\u4eba\u7686\u53ef\u6aa2\u8996\u8207\u8907\u88fd(clone). \u4f46\u53ea\u6709\u6210\u54e1\u6709RW+\u8207\u63a8\u9001(push)\u529f\u80fd.
+gb.namedPushPolicy = \u9650\u5236\u63a8\u9001(Push)(\u6307\u5b9a\u5e33\u865f)
+gb.namedPushPolicyDescription = \u6240\u6709\u4eba\u7686\u53ef\u6aa2\u8996\u8207\u8907\u88fd(clone)\u7248\u672c\u5eab. \u53ef\u53e6\u5916\u6307\u5b9a\u8ab0\u80fd\u5920\u6709\u63a8\u9001\u529f\u80fd(push)
+gb.clonePolicy = \u9650\u5236\u8907\u88fd(Clone)\u8207\u63a8\u9001(Push)
+gb.clonePolicyDescription = \u6240\u6709\u4eba\u7686\u53ef\u770b\u7248\u672c\u5eab. \u53ef\u53e6\u5916\u6307\u5b9a\u8ab0\u6709\u8907\u88fd(clone)\u8207\u63a8\u9001(push)\u6b0a\u9650
+gb.viewPolicy = \u9650\u5236\u6aa2\u8996(view),\u8907\u88fd(clone)\u8207\u63a8\u9001(push)
+gb.viewPolicyDescription = \u9078\u64c7\u53ef\u4ee5\u5728\u7248\u672c\u5eab\u6aa2\u8996,\u8907\u88fd(clone)\u8207\u63a8\u9001(push)\u7684\u4f7f\u7528\u8005, \u9664\u6b64\u4e4b\u5916\u5176\u4ed6\u4eba\u7686\u7121\u6b0a\u9650
+gb.initialCommit = \u521d\u6b21\u63d0\u4ea4
+gb.initialCommitDescription = \u4ee5\u4e0b\u6b65\u9a5f\u5c07\u99ac\u4e0a\u57f7\u884c<code>git clone</code>.\u5982\u679c\u4f60\u672c\u6a5f\u5df2\u6709\u6b64\u7248\u672c\u5eab\u4e14\u57f7\u884c\u904e<code>git init</code>,\u8acb\u8df3\u904e\u6b64\u6b65\u9a5f.
+gb.initWithReadme = \u5305\u542bREADME\u6587\u4ef6
+gb.initWithReadmeDescription = \u7248\u672c\u5eab\u5c07\u7522\u751f\u7c21\u55aeREADME\u6587\u4ef6
+gb.initWithGitignore = \u5305\u542b .gitignore \u6a94\u6848
+gb.initWithGitignoreDescription = \u65b0\u589e\u4e00\u500b\u8a2d\u5b9a\u6a94\u7528\u4f86\u6307\u5b9a\u54ea\u4e9b\u6a94\u6848\u6216\u76ee\u9304\u9700\u8981\u5ffd\u7565
+gb.pleaseSelectGitIgnore = \u8acb\u9078\u64c7\u4e00\u500b.gitignore\u6a94\u6848
+gb.receive = \u63a5\u6536
+gb.permissions = \u6b0a\u9650
+gb.ownersDescription = \u6240\u6709\u8005\u53ef\u4ee5\u7ba1\u7406\u7248\u672c\u5eab,\u4f46\u662f\u4e0d\u5141\u8a31\u4fee\u6539\u540d\u7a31(\u500b\u4eba\u7248\u672c\u5eab\u4f8b\u5916)
+gb.userPermissionsDescription = \u4f60\u53ef\u4ee5\u91dd\u5c0d\u5e33\u865f\u8a2d\u5b9a\u6b0a\u9650(\u9019\u4e9b\u8a2d\u5b9a\u5c07\u8986\u84cb\u5718\u968a\u6216\u5176\u4ed6\u6b0a\u9650)
+gb.teamPermissionsDescription = \u4f60\u53ef\u4ee5\u6307\u5b9a\u5718\u968a\u6b0a\u9650.\u9019\u4e9b\u8a2d\u5b9a\u5c07\u6703\u53d6\u4ee3\u539f\u672c\u5718\u968a\u9810\u8a2d\u6b0a\u9650
+gb.ticketSettings = \u4efb\u52d9\u5167\u5bb9\u8a2d\u5b9a
+gb.receiveSettings = \u8a2d\u5b9a\u63a5\u6536\u65b9\u5f0f
+gb.receiveSettingsDescription = \u63a7\u7ba1\u63a8\u9001\u5230\u7248\u672c\u5eab\u7684\u63a5\u6536\u65b9\u5f0f
+gb.preReceiveDescription = \u63a5\u5230\u63d0\u4ea4\u7533\u8acb\u5f8c,<em>\u4f46\u5728\u9084\u6c92\u6709\u66f4\u65b0refs\u524d</em>, \u5c07\u6703\u57f7\u884cPre-receive hook. <p>This is the appropriate hook for rejecting a push.</p>
+gb.postReceiveDescription = \u63a5\u5230\u63d0\u4ea4\u7533\u8acb\u5f8c,<em>\u4e26\u4e14\u5728refs\u5b8c\u7562\u5f8c</em>, \u5c07\u6703\u57f7\u884cPost-receive hook..<p>This is the appropriate hook for notifications, build triggers, etc.</p>
+gb.federationStrategyDescription = \u63a7\u5236\u5982\u4f55\u5c07\u7248\u672c\u5eab\u8207\u5176\u4ed6Gitblit\u7248\u63a7\u4f3a\u670d\u5668\u4e32\u9023
+gb.federationSetsDescription = \u6b64\u7248\u672c\u5eab\u5c07\u5305\u542b\u65bc\u6307\u5b9a\u7684federation sets
+gb.miscellaneous = \u5176\u4ed6
+gb.originDescription = \u6b64\u7248\u672c\u5eabURL\u5df2\u7d93\u88ab\u8907\u88fd(cloned)\u4e86
+gb.gc = \u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u5668
+gb.garbageCollection = \u56de\u6536\u7cfb\u7d71\u8cc7\u6e90
+gb.garbageCollectionDescription = \u7cfb\u7d71\u8cc7\u6e90\u56de\u6536\u529f\u80fd\u5c07\u6703\u6574\u9813\u9b06\u6563\u7528\u6236\u7aef\u63a8\u9001(push)\u7684\u7269\u4ef6, \u4e5f\u6703\u79fb\u9664\u7248\u672c\u5eab\u4e0a\u7121\u7528\u7684\u7269\u4ef6
+gb.commitMessageRendererDescription = \u63d0\u4ea4\u8a0a\u606f\u53ef\u4ee5\u4f7f\u7528\u6587\u5b57\u6216\u662f\u6a19\u8a18\u8a9e\u8a00(markup)\u5448\u73fe
+gb.preferences = \u9810\u8a2d\u5e38\u7528\u503c
+gb.accountPreferences = \u5e33\u865f\u8a2d\u5b9a
+gb.accountPreferencesDescription = \u8a2d\u5b9a\u5e33\u865f\u9810\u8a2d\u503c
+gb.languagePreference = \u5e38\u7528\u8a9e\u8a00
+gb.languagePreferenceDescription = \u9078\u64c7\u8a9e\u7cfb
+gb.emailMeOnMyTicketChanges = \u4efb\u52d9\u82e5\u6709\u8b8a\u66f4,\u8acb\u7acb\u5373(email)\u901a\u77e5\u6211
+gb.emailMeOnMyTicketChangesDescription =\u6211\u8655\u7406\u904e\u7684\u4efb\u52d9\u8acbemail\u901a\u77e5\u6211
+gb.displayNameDescription = \u5e0c\u671b\u986f\u793a\u7684\u540d\u7a31
+gb.emailAddressDescription = \u7528\u4f86\u63a5\u6536\u901a\u77e5\u7684\u4e3b\u8981\u96fb\u5b50\u90f5\u4ef6
+gb.sshKeys = SSH Keys
+gb.sshKeysDescription = SSH \u516c\u958b\u91d1\u9470\u662f\u5bc6\u78bc\u8a8d\u8b49\u5916\u66f4\u5b89\u5168\u7684\u9078\u9805
+gb.addSshKey = \u65b0\u589e SSH Key
+gb.key = \u91d1\u9470
+gb.sshKeyCommentDescription = \u8acb\u8f38\u5165\u5099\u8a3b, \u82e5\u7121\u5099\u8a3b, \u5c07\u81ea\u8a02\u586b\u5165key data
+gb.sshKeyPermissionDescription = \u6307\u5b9a\u8a72SSH key\u6240\u64c1\u6709\u7684\u5b58\u53d6\u6b0a\u9650
+gb.transportPreference = \u9810\u8a2d\u901a\u8a0a\u5354\u5b9a
+gb.transportPreferenceDescription = \u8a2d\u5b9a\u4f60\u5e38\u7528\u7684\u9023\u7dda\u7528\u4f86\u8907\u88fd(clone)
+gb.blinkComparator = Blink comparator
+gb.deleteRepositoryDescription = \u7248\u672c\u5eab\u522a\u9664\u5c07\u7121\u6cd5\u9084\u539f
+gb.deleteRepositoryHeader = \u522a\u9664\u7248\u672c\u5eab
+gb.diffCopiedFile = \u6a94\u6848\u7531 {0} \u8907\u88fd
+gb.diffDeletedFile = \u6a94\u6848\u5df2\u522a\u9664
+gb.diffDeletedFileSkipped = (\u522a\u9664)
+gb.diffFileDiffTooLarge = \u6a94\u6848\u592a\u5927
+gb.diffNewFile = \u6bd4\u5c0d\u65b0\u6a94\u6848
+gb.diffRenamedFile = \u6a94\u540d\u7531 {0} \u4fee\u6539
+gb.diffTruncated = Diff truncated after the above file
+gb.ignore_whitespace =\u5ffd\u7565\u7a7a\u767d
+gb.imgdiffSubtract = Subtract (black = identical)
+gb.maintenanceTickets = \u7dad\u8b77
+gb.missingIntegrationBranchMore = \u76ee\u6a19\u5206\u652f\u4e0d\u5728\u6b64\u7248\u672c\u5eab
+gb.opacityAdjust = Adjust opacity
+gb.priority = \u512a\u5148
+gb.severity = \u91cd\u8981
+gb.show_whitespace = \u986f\u793a\u7a7a\u767d
+gb.sortHighestPriority = \u6700\u9ad8
+gb.sortHighestSeverity = \u6700\u91cd\u8981
+gb.sortLowestPriority = \u6700\u4f4e
+gb.sortLowestSeverity = \u6700\u4e0d\u91cd\u8981
+gb.ticketStatus = \u72c0\u614b
+gb.allRepositories = \u6240\u6709\u7248\u672c\u5eab
+gb.oid = \u7de8\u865f
+gb.filestore = \u5927\u6a94\u6848\u5340
+gb.filestoreStats = \u5927\u6a94\u6848\u5340(Filestore)\u5305\u542b {0} \u6a94\u6848\u5bb9\u91cf {1}. (\u9084\u5269\u4e0b{2}\u53ef\u7528)
+gb.statusChangedOn = \u4fee\u6539\u65e5\u671f
+gb.statusChangedBy = \u4fee\u6539\u8005
+gb.filestoreHelp = \u6309\u6b64\u4e86\u89e3\u5927\u6a94\u6848\u5340(FileStore)\u5132\u5b58\u529f\u80fd
+gb.editFile = \u7de8\u8f2f\u6a94\u6848
+gb.continueEditing = \u7e7c\u7e8c\u7de8\u8f2f
+gb.commitChanges = Commit Changes
+gb.fileNotMergeable = \u7121\u6cd5\u63d0\u4ea4 {0}. \u6a94\u6848\u7121\u6cd5\u81ea\u52d5\u5408\u4f75.
+gb.fileCommitted = \u6210\u529f\u63d0\u4ea4
+gb.deletePatchset = \u522a\u9664 Patchset {0}
+gb.deletePatchsetSuccess = \u5df2\u522a\u9664 Patchset {0}.
+gb.deletePatchsetFailure = \u522a\u9664 Patchset {0} \u932f\u8aa4.
+gb.referencedByCommit = Referenced by commit.
+gb.referencedByTicket = Referenced by ticket.
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.html b/src/main/java/com/gitblit/wicket/pages/BasePage.html
index b998428c..4dbc2e57 100644
--- a/src/main/java/com/gitblit/wicket/pages/BasePage.html
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.html
@@ -17,6 +17,7 @@
<link rel="stylesheet" href="fontawesome/css/font-awesome.min.css"/>
<link rel="stylesheet" href="octicons/octicons.css"/>
<link rel="stylesheet" type="text/css" href="gitblit.css"/>
+ <link rel="stylesheet" type="text/css" href="bootstrap-fixes.css"/>
</wicket:head>
<body>
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
index 7a55b9f5..2c881efc 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -123,7 +123,8 @@
<div wicket:id="acceptNewTickets"></div>
<div wicket:id="requireApproval"></div>
<div wicket:id="mergeTo"></div>
-
+ <div wicket:id="mergeType"></div>
+
</div>
<!-- federation -->
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
index 6bcf6f51..bf3eea8b 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -56,6 +56,7 @@ import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.CommitMessageRenderer;
import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.Constants.MergeType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
@@ -458,6 +459,11 @@ public class EditRepositoryPage extends RootSubPage {
getString("gb.mergeToDescription"),
new PropertyModel<String>(repositoryModel, "mergeTo"),
availableBranches));
+ form.add(new ChoiceOption<MergeType>("mergeType",
+ getString("gb.mergeType"),
+ getString("gb.mergeTypeDescription"),
+ new PropertyModel<MergeType>(repositoryModel, "mergeType"),
+ Arrays.asList(MergeType.values())));
//
// RECEIVE
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
index 220bee3f..72dee6b6 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -156,7 +156,7 @@ public class EditUserPage extends RootSubPage {
}
// change the cookie
- userModel.cookie = StringUtils.getSHA1(userModel.username + password);
+ userModel.cookie = userModel.createCookie();
// Optionally store the password MD5 digest.
String type = app().settings().getString(Keys.realm.passwordStorage, "md5");
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
index cd049f4d..e2133966 100644
--- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
@@ -1405,14 +1405,14 @@ public class TicketPage extends RepositoryPage {
boolean allowMerge;
if (repository.requireApproval) {
- // rpeository requires approval
+ // repository requires approval
allowMerge = ticket.isOpen() && ticket.isApproved(patchset);
} else {
- // vetos are binding
+ // vetoes are binding
allowMerge = ticket.isOpen() && !ticket.isVetoed(patchset);
}
- MergeStatus mergeStatus = JGitUtils.canMerge(getRepository(), patchset.tip, ticket.mergeTo);
+ MergeStatus mergeStatus = JGitUtils.canMerge(getRepository(), patchset.tip, ticket.mergeTo, repository.mergeType);
if (allowMerge) {
if (MergeStatus.MERGEABLE == mergeStatus) {
// patchset can be cleanly merged to integration branch OR has already been merged
diff --git a/src/main/java/com/gitblit/wicket/panels/PagerPanel.java b/src/main/java/com/gitblit/wicket/panels/PagerPanel.java
index 2d774c41..d1214cae 100644
--- a/src/main/java/com/gitblit/wicket/panels/PagerPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/PagerPanel.java
@@ -48,7 +48,7 @@ public class PagerPanel extends Panel {
deltas = new int[] { -2, -1, 0, 1, 2 };
}
- if (totalPages > 0) {
+ if (totalPages > 0 && currentPage > 1) {
pages.add(new PageObject("\u2190", currentPage - 1));
}
for (int delta : deltas) {
@@ -57,7 +57,7 @@ public class PagerPanel extends Panel {
pages.add(new PageObject("" + page, page));
}
}
- if (totalPages > 0) {
+ if (totalPages > 0 && currentPage < totalPages) {
pages.add(new PageObject("\u2192", currentPage + 1));
}
@@ -75,6 +75,7 @@ public class PagerPanel extends Panel {
item.add(link);
if (pageItem.page == currentPage || pageItem.page < 1 || pageItem.page > totalPages) {
WicketUtils.setCssClass(item, "disabled");
+ link.setEnabled(false);
}
}
};
diff --git a/src/main/java/welcome_zh_TW.mkd b/src/main/java/welcome_zh_TW.mkd
index eaaee652..ec102014 100644
--- a/src/main/java/welcome_zh_TW.mkd
+++ b/src/main/java/welcome_zh_TW.mkd
@@ -1,3 +1,3 @@
## 歡迎來到Gitblit版本控管伺服器
-一個快速讓您能存放自己Git文件庫的解決方案 [Git](http://www.git-scm.com)
+一個快速讓您擁有版本控管伺服器的解決方案 : 使用[Git](http://www.git-scm.com)
diff --git a/src/main/resources/bootstrap-fixes.css b/src/main/resources/bootstrap-fixes.css
new file mode 100644
index 00000000..c9b6154b
--- /dev/null
+++ b/src/main/resources/bootstrap-fixes.css
@@ -0,0 +1,25 @@
+/**
+ * Disabled links in a PagerPanel. Bootstrap 2.0.4 only handles <a>, but not <span>. Wicket renders disabled links as spans.
+ * The .pagination rules here are identical to the ones for <a> in bootstrap.css, but for <span>.
+ */
+.pagination span {
+ float: left;
+ padding: 0 14px;
+ line-height: 34px;
+ text-decoration: none;
+ border: 1px solid #ddd;
+ border-left-width: 0;
+}
+
+.pagination li:first-child span {
+ border-left-width: 1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.pagination li:last-child span {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
diff --git a/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java b/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
index e40f1057..bc7aad49 100644
--- a/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
@@ -15,8 +15,14 @@
*/
package com.gitblit.tests;
+import java.util.HashMap;
+import java.util.Map;
+
import org.junit.Test;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.tests.mock.MemorySettings;
import com.gitblit.utils.MarkdownUtils;
public class MarkdownUtilsTest extends GitblitUnitTest {
@@ -39,4 +45,70 @@ public class MarkdownUtilsTest extends GitblitUnitTest {
assertEquals("<table><tr><td>&lt;test&gt;</td></tr></table>",
MarkdownUtils.transformMarkdown("<table><tr><td>&lt;test&gt;</td></tr></table>"));
}
-} \ No newline at end of file
+
+
+ @Test
+ public void testUserMentions() {
+ IStoredSettings settings = getSettings();
+ String repositoryName = "test3";
+ String mentionHtml = "<strong><a href=\"http://localhost/user/%1$s\">@%1$s</a></strong>";
+
+ String input = "@j.doe";
+ String output = "<p>" + String.format(mentionHtml, "j.doe") + "</p>";
+ assertEquals(output, MarkdownUtils.transformGFM(settings, input, repositoryName));
+
+ input = " @j.doe";
+ output = "<p>" + String.format(mentionHtml, "j.doe") + "</p>";
+ assertEquals(output, MarkdownUtils.transformGFM(settings, input, repositoryName));
+
+ input = "@j.doe.";
+ output = "<p>" + String.format(mentionHtml, "j.doe") + ".</p>";
+ assertEquals(output, MarkdownUtils.transformGFM(settings, input, repositoryName));
+
+ input = "To @j.doe: ask @jim.beam!";
+ output = "<p>To " + String.format(mentionHtml, "j.doe")
+ + ": ask " + String.format(mentionHtml, "jim.beam") + "!</p>";
+ assertEquals(output, MarkdownUtils.transformGFM(settings, input, repositoryName));
+
+ input = "@sta.rt\n"
+ + "\n"
+ + "User mentions in tickets are broken.\n"
+ + "So:\n"
+ + "@mc_guyver can fix this.\n"
+ + "@j.doe, can you test after the fix by @m+guyver?\n"
+ + "Please review this, @jim.beam!\n"
+ + "Was reported by @jill and @j!doe from jane@doe yesterday.\n"
+ + "\n"
+ + "@jack.daniels can vote for john@wayne.name hopefully.\n"
+ + "@en.de";
+ output = "<p>" + String.format(mentionHtml, "sta.rt") + "</p>"
+ + "<p>" + "User mentions in tickets are broken.<br/>"
+ + "So:<br/>"
+ + String.format(mentionHtml, "mc_guyver") + " can fix this.<br/>"
+ + String.format(mentionHtml, "j.doe") + ", can you test after the fix by " + String.format(mentionHtml, "m+guyver") + "?<br/>"
+ + "Please review this, " + String.format(mentionHtml, "jim.beam") + "!<br/>"
+ + "Was reported by " + String.format(mentionHtml, "jill")
+ + " and " + String.format(mentionHtml, "j!doe")
+ + " from <a href=\"mailto:&#106;a&#110;&#x65;&#x40;&#x64;&#x6f;&#101;\">&#106;a&#110;&#x65;&#x40;&#x64;&#x6f;&#101;</a> yesterday."
+ + "</p>"
+ + "<p>" + String.format(mentionHtml, "jack.daniels") + " can vote for "
+ + "<a href=\"mailto:&#x6a;&#x6f;h&#110;&#x40;&#119;a&#121;&#110;&#101;.&#110;a&#x6d;&#101;\">&#x6a;&#x6f;h&#110;&#x40;&#119;a&#121;&#110;&#101;.&#110;a&#x6d;&#101;</a> hopefully.<br/>"
+ + String.format(mentionHtml, "en.de")
+ + "</p>";
+ assertEquals(output, MarkdownUtils.transformGFM(settings, input, repositoryName));
+
+ }
+
+
+
+
+ private MemorySettings getSettings() {
+ Map<String, Object> backingMap = new HashMap<String, Object>();
+
+ backingMap.put(Keys.web.canonicalUrl, "http://localhost");
+ backingMap.put(Keys.web.shortCommitIdLength, "7");
+
+ MemorySettings ms = new MemorySettings(backingMap);
+ return ms;
+ }
+}
diff --git a/src/test/java/com/gitblit/utils/SecureRandomTest.java b/src/test/java/com/gitblit/utils/SecureRandomTest.java
new file mode 100644
index 00000000..c4098c2f
--- /dev/null
+++ b/src/test/java/com/gitblit/utils/SecureRandomTest.java
@@ -0,0 +1,33 @@
+package com.gitblit.utils;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class SecureRandomTest {
+
+ @Test
+ public void testRandomBytes() {
+ SecureRandom sr = new SecureRandom();
+ byte[] bytes1 = sr.randomBytes(10);
+ assertEquals(10, bytes1.length);
+ byte[] bytes2 = sr.randomBytes(10);
+ assertEquals(10, bytes2.length);
+ assertFalse(Arrays.equals(bytes1, bytes2));
+
+ assertEquals(0, sr.randomBytes(0).length);
+ assertEquals(200, sr.randomBytes(200).length);
+ }
+
+ @Test
+ public void testNextBytes() {
+ SecureRandom sr = new SecureRandom();
+ byte[] bytes1 = new byte[32];
+ sr.nextBytes(bytes1);
+ byte[] bytes2 = new byte[32];
+ sr.nextBytes(bytes2);
+ assertFalse(Arrays.equals(bytes1, bytes2));
+ }
+}