diff options
author | James Moger <james.moger@gitblit.com> | 2012-08-10 17:46:11 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2012-08-10 17:46:11 -0400 |
commit | eb870fc034460c2bab69039b21049d332a002ca1 (patch) | |
tree | fa16e26f61469334e6bfb1353de283860686c129 | |
parent | 88fb67f6cfdef7a3d44691aca623d3486fec3655 (diff) | |
download | gitblit-eb870fc034460c2bab69039b21049d332a002ca1.tar.gz gitblit-eb870fc034460c2bab69039b21049d332a002ca1.zip |
Submodules support
21 files changed, 432 insertions, 58 deletions
diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index a5a47b78..836ac05c 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -37,6 +37,18 @@ git.searchRecursionDepth = -1 # SINCE 1.0.1
git.searchExclusions =
+# List of regex url patterns for extracting a repository name when locating
+# submodules.
+# e.g. git.submoduleUrlPatterns = .*?://github.com/(.*) will extract
+# *gitblit/gitblit.git* from *git://github.com/gitblit/gitblit.git*
+# If no matches are found then the submodule repository name is assumed to be
+# whatever trails the last / character. (e.g. gitblit.git).
+#
+# SPACE-DELIMITED
+# CASE-SENSITIVE
+# SINCE 1.0.1
+git.submoduleUrlPatterns = .*?://github.com/(.*)
+
# Allow push/pull over http/https with JGit servlet.
# If you do NOT want to allow Git clients to clone/push to Gitblit set this
# to false. You might want to do this if you are only using ssh:// or git://.
diff --git a/docs/01_features.mkd b/docs/01_features.mkd index a4fc802b..fb964fa3 100644 --- a/docs/01_features.mkd +++ b/docs/01_features.mkd @@ -23,6 +23,7 @@ - LDAP authentication and optional LDAP-controlled Team memberships
- Gravatar integration
- Git-notes display support
+- Submodule support
- gh-pages display support (Jekyll is not supported)
- Branch metrics (uses Google Charts)
- HEAD and Branch RSS feeds
diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index e94d8c15..ab81cb3e 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -11,12 +11,11 @@ If you are updating from an earlier release AND you have indexed branches with t #### fixes
-- Support StartTLS in LdapUserService (Steffen Gebert, issue 122)
-- Do not index blobs in submodules (issue 119)
+- Do not index submodule links (issue 119)
- Restore original user or team object on failure to update (issue 118)
- Fixes to relative path determination in repository search algorithm for symlinks (issue 116)
- Fix to GitServlet to allow pushing to symlinked repositories (issue 116)
-- Repository URL uses `X-Forwarded-Proto` and `X-Forwarded-Port`, if available, for reverse proxy configurations (issue 115)
+- Repository URL now uses `X-Forwarded-Proto` and `X-Forwarded-Port`, if available, for reverse proxy configurations (issue 115)
- Output real RAW content, not simulated RAW content (issue 114)
- Fixed Lucene charset encoding bug when reindexing a repository (issue 112)
- Fixed search box linking to Lucene page for nested repository on Tomcat (issue 111)
@@ -25,9 +24,17 @@ If you are updating from an earlier release AND you have indexed branches with t #### additions
-- Added a repository setting to control authorization as AUTHENTICATED or NAMED.
-NAMED is the original behavior for authorizing against a list of permitted users or permitted teams.
-AUTHENTICATED allows restricted access for any authenticated user.
+- Preliminary bare repository submodule support
+ **New:** *git.submoduleUrlPatterns=*
+ - *git.submoduleUrlPatterns* is a space-delimited list of regular expressions for extracting a repository name from a submodule url.
+ For example, `git.submoduleUrlPatterns = .*?://github.com/(.*)` would extract *gitblit/gitblit.git* from *git://github.git/gitblit/gitblit.git*
+ **Note:** You may not need this control to work with submodules, but it is there if you do.
+ - If there are no matches from *git.submoduleUrlPatterns* then the repository name is assumed to be whatever comes after the last `/` character *(e.g. gitblit.git)*
+ - Gitblit will try to locate this repository relative to the current repository *(e.g. myfolder/myrepo.git, myfolder/mysubmodule.git)* and then at the root level *(mysubmodule.git)* if that fails.
+ - Submodule references in a working copy will be properly identified as gitlinks, but Gitblit will not traverse into the working copy submodule repository.
+- Added a repository setting to control authorization as AUTHENTICATED or NAMED. (issue 117)
+NAMED is the original behavior for authorizing against a list of permitted users or permitted teams.
+AUTHENTICATED allows restricted access for any authenticated user. This is a looser authorization control.
- Added default authorization control setting (AUTHENTICATED or NAMED)
**New:** *git.defaultAuthorizationControl=NAMED*
- Added setting to control how deep Gitblit will recurse into *git.repositoriesFolder* looking for repositories (issue 103)
@@ -36,11 +43,18 @@ AUTHENTICATED allows restricted access for any authenticated user. **New:** *git.searchExclusions=*
- Blob page now supports displaying images (issue 6)
- Non-image binary files can now be downloaded using the RAW link
+- Support StartTLS in LdapUserService (Steffen Gebert, issue 122)
#### changes
+- Line breaks inserted for readability in raw Markdown content display in the event of a parsing/transformation error. An error message is now displayed prepended to the raw content.
+- Improve UTF-8 reading for Markdown files
- Updated Polish translation
+<hr/>
+
+### Older Releases
+
**1.0.0** *released 2012-07-14*
#### fixes
@@ -104,8 +118,6 @@ Make sure to properly set *web.blobEncodings* before starting Gitblit if you are <hr/>
-### Older Releases
-
**0.9.3** *released 2012-04-11*
#### fixes
diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 26f30f91..f52f229f 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -910,6 +910,21 @@ public class GitBlit implements ServletContextListener { r.close();
return model;
}
+
+ /**
+ * Determines if this server has the requested repository.
+ *
+ * @param name
+ * @return true if the repository exists
+ */
+ public boolean hasRepository(String repositoryName) {
+ Repository r = getRepository(repositoryName, false);
+ if (r == null) {
+ return false;
+ }
+ r.close();
+ return true;
+ }
/**
* Returns the size in bytes of the repository. Gitblit caches the
diff --git a/src/com/gitblit/models/PathModel.java b/src/com/gitblit/models/PathModel.java index 9bb7eb73..8692359c 100644 --- a/src/com/gitblit/models/PathModel.java +++ b/src/com/gitblit/models/PathModel.java @@ -35,14 +35,16 @@ public class PathModel implements Serializable, Comparable<PathModel> { public final String path;
public final long size;
public final int mode;
+ public final String objectId;
public final String commitId;
public boolean isParentPath;
- public PathModel(String name, String path, long size, int mode, String commitId) {
+ public PathModel(String name, String path, long size, int mode, String objectId, String commitId) {
this.name = name;
this.path = path;
this.size = size;
this.mode = mode;
+ this.objectId = objectId;
this.commitId = commitId;
}
@@ -102,9 +104,9 @@ public class PathModel implements Serializable, Comparable<PathModel> { public final ChangeType changeType;
- public PathChangeModel(String name, String path, long size, int mode, String commitId,
- ChangeType type) {
- super(name, path, size, mode, commitId);
+ public PathChangeModel(String name, String path, long size, int mode, String objectId,
+ String commitId, ChangeType type) {
+ super(name, path, size, mode, objectId, commitId);
this.changeType = type;
}
diff --git a/src/com/gitblit/models/SubmoduleModel.java b/src/com/gitblit/models/SubmoduleModel.java new file mode 100644 index 00000000..47f84b95 --- /dev/null +++ b/src/com/gitblit/models/SubmoduleModel.java @@ -0,0 +1,47 @@ +/*
+ * Copyright 2012 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.models;
+
+import java.io.Serializable;
+
+/**
+ * SubmoduleModel is a serializable model class that represents a git submodule
+ * definition.
+ *
+ * @author James Moger
+ *
+ */
+public class SubmoduleModel implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String name;
+ public final String path;
+ public final String url;
+
+ public boolean hasSubmodule;
+ public String gitblitPath;
+
+ public SubmoduleModel(String name, String path, String url) {
+ this.name = name;
+ this.path = path;
+ this.url = url;
+ }
+
+ public String toString() {
+ return path + "=" + url;
+ }
+}
\ No newline at end of file diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index 90e6a76b..5eb83edb 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -45,7 +45,9 @@ import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
+import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
@@ -87,6 +89,7 @@ import com.gitblit.models.GitNote; import com.gitblit.models.PathModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
+import com.gitblit.models.SubmoduleModel;
/**
* Collection of static methods for retrieving information from a repository.
@@ -732,7 +735,8 @@ public class JGitUtils { tw.addTree(commit.getTree());
while (tw.next()) {
list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw
- .getRawMode(0), commit.getId().getName(), ChangeType.ADD));
+ .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),
+ ChangeType.ADD));
}
tw.release();
} else {
@@ -745,15 +749,15 @@ public class JGitUtils { for (DiffEntry diff : diffs) {
if (diff.getChangeType().equals(ChangeType.DELETE)) {
list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff
- .getNewMode().getBits(), commit.getId().getName(), diff
+ .getNewMode().getBits(), null, commit.getId().getName(), diff
.getChangeType()));
} else if (diff.getChangeType().equals(ChangeType.RENAME)) {
list.add(new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff
- .getNewMode().getBits(), commit.getId().getName(), diff
+ .getNewMode().getBits(), null, commit.getId().getName(), diff
.getChangeType()));
} else {
list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff
- .getNewMode().getBits(), commit.getId().getName(), diff
+ .getNewMode().getBits(), null, commit.getId().getName(), diff
.getChangeType()));
}
}
@@ -846,15 +850,16 @@ public class JGitUtils { } else {
name = tw.getPathString().substring(basePath.length() + 1);
}
+ ObjectId objectId = tw.getObjectId(0);
try {
- if (!tw.isSubtree()) {
- size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);
+ if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {
+ size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB);
}
} catch (Throwable t) {
error(t, null, "failed to retrieve blob size for " + tw.getPathString());
}
return new PathModel(name, tw.getPathString(), size, tw.getFileMode(0).getBits(),
- commit.getName());
+ objectId.getName(), commit.getName());
}
/**
@@ -871,13 +876,10 @@ public class JGitUtils { } else if (FileMode.EXECUTABLE_FILE.equals(mode)) {
return "-rwxr-xr-x";
} else if (FileMode.SYMLINK.equals(mode)) {
- // FIXME symlink permissions
return "symlink";
} else if (FileMode.GITLINK.equals(mode)) {
- // FIXME gitlink permissions
- return "gitlink";
+ return "submodule";
}
- // FIXME missing permissions
return "missing";
}
@@ -1533,6 +1535,62 @@ public class JGitUtils { }
return branch;
}
+
+ /**
+ * Returns the list of submodules for this repository.
+ *
+ * @param repository
+ * @param commit
+ * @return list of submodules
+ */
+ public static List<SubmoduleModel> getSubmodules(Repository repository, String commitId) {
+ RevCommit commit = getCommit(repository, commitId);
+ return getSubmodules(repository, commit.getTree());
+ }
+
+ /**
+ * Returns the list of submodules for this repository.
+ *
+ * @param repository
+ * @param commit
+ * @return list of submodules
+ */
+ public static List<SubmoduleModel> getSubmodules(Repository repository, RevTree tree) {
+ List<SubmoduleModel> list = new ArrayList<SubmoduleModel>();
+ byte [] blob = getByteContent(repository, tree, ".gitmodules");
+ if (blob == null) {
+ return list;
+ }
+ try {
+ BlobBasedConfig config = new BlobBasedConfig(repository.getConfig(), blob);
+ for (String module : config.getSubsections("submodule")) {
+ String path = config.getString("submodule", module, "path");
+ String url = config.getString("submodule", module, "url");
+ list.add(new SubmoduleModel(module, path, url));
+ }
+ } catch (ConfigInvalidException e) {
+ LOGGER.error("Failed to load .gitmodules file for " + repository.getDirectory(), e);
+ }
+ return list;
+ }
+
+ /**
+ * Returns the submodule definition for the specified path at the specified
+ * commit. If no module is defined for the path, null is returned.
+ *
+ * @param repository
+ * @param commit
+ * @param path
+ * @return a submodule definition or null if there is no submodule
+ */
+ public static SubmoduleModel getSubmoduleModel(Repository repository, String commitId, String path) {
+ for (SubmoduleModel model : getSubmodules(repository, commitId)) {
+ if (model.path.equals(path)) {
+ return model;
+ }
+ }
+ return null;
+ }
/**
* Returns the list of notes entered about the commit from the refs/notes
diff --git a/src/com/gitblit/utils/MarkdownUtils.java b/src/com/gitblit/utils/MarkdownUtils.java index a3a18592..828225db 100644 --- a/src/com/gitblit/utils/MarkdownUtils.java +++ b/src/com/gitblit/utils/MarkdownUtils.java @@ -20,6 +20,7 @@ import java.io.Reader; import java.io.StringReader;
import java.io.StringWriter;
+import org.slf4j.LoggerFactory;
import org.tautua.markdownpapers.Markdown;
import org.tautua.markdownpapers.parser.ParseException;
@@ -44,6 +45,8 @@ public class MarkdownUtils { String html = transformMarkdown(reader);
reader.close();
return html;
+ } catch (IllegalArgumentException e) {
+ throw new java.text.ParseException(e.getMessage(), 0);
} catch (NullPointerException p) {
throw new java.text.ParseException("Markdown string is null!", 0);
}
@@ -65,6 +68,7 @@ public class MarkdownUtils { md.transform(markdownReader, writer);
return writer.toString().trim();
} catch (ParseException p) {
+ LoggerFactory.getLogger(MarkdownUtils.class).error("MarkdownPapers failed to parse Markdown!", p);
throw new java.text.ParseException(p.getMessage(), 0);
} finally {
try {
diff --git a/src/com/gitblit/utils/StringUtils.java b/src/com/gitblit/utils/StringUtils.java index 412a920f..3972f202 100644 --- a/src/com/gitblit/utils/StringUtils.java +++ b/src/com/gitblit/utils/StringUtils.java @@ -33,6 +33,8 @@ import java.util.Comparator; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
@@ -599,4 +601,28 @@ public class StringUtils { }
return value;
}
+
+ /**
+ * Attempt to extract a repository name from a given url using regular
+ * expressions. If no match is made, then return whatever trails after
+ * the final / character.
+ *
+ * @param regexUrls
+ * @return a repository path
+ */
+ public static String extractRepositoryPath(String url, String... urlpatterns) {
+ for (String urlPattern : urlpatterns) {
+ Pattern p = Pattern.compile(urlPattern);
+ Matcher m = p.matcher(url);
+ while (m.find()) {
+ String repositoryPath = m.group(1);
+ return repositoryPath;
+ }
+ }
+ // last resort
+ if (url.lastIndexOf('/') > -1) {
+ return url.substring(url.lastIndexOf('/') + 1);
+ }
+ return url;
+ }
}
\ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index bcd63370..f9480a8f 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -312,4 +312,5 @@ gb.duration.oneYear = 1 year gb.duration.years = {0} years
gb.authorizationControl = authorization control
gb.allowAuthenticatedDescription = grant restricted access to all authenticated users
-gb.allowNamedDescription = grant restricted access to named users or teams
\ No newline at end of file +gb.allowNamedDescription = grant restricted access to named users or teams
+gb.markdownFailure = Failed to parse Markdown content!
\ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebApp_es.properties b/src/com/gitblit/wicket/GitBlitWebApp_es.properties index fec51b8b..2140f525 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_es.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_es.properties @@ -312,4 +312,5 @@ gb.duration.oneYear = 1 a\u00F1o gb.duration.years = {0} a\u00F1os
gb.authorizationControl = authorization control
gb.allowAuthenticatedDescription = grant restricted access to all authenticated users
-gb.allowNamedDescription = grant restricted access to named users or teams
\ No newline at end of file +gb.allowNamedDescription = grant restricted access to named users or teams
+gb.markdownFailure = Failed to parse Markdown content!
\ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebApp_ja.properties b/src/com/gitblit/wicket/GitBlitWebApp_ja.properties index f0fe3e84..04e2bf35 100755 --- a/src/com/gitblit/wicket/GitBlitWebApp_ja.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_ja.properties @@ -312,4 +312,5 @@ gb.duration.oneYear = 1 year gb.duration.years = {0} years
gb.authorizationControl = authorization control
gb.allowAuthenticatedDescription = grant restricted access to all authenticated users
-gb.allowNamedDescription = grant restricted access to named users or teams
\ No newline at end of file +gb.allowNamedDescription = grant restricted access to named users or teams
+gb.markdownFailure = Failed to parse Markdown content!
\ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebApp_pl.properties b/src/com/gitblit/wicket/GitBlitWebApp_pl.properties index 9486595c..7c35dabe 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_pl.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_pl.properties @@ -312,4 +312,5 @@ gb.duration.oneYear = rok gb.duration.years = {0} lat gb.authorizationControl = authorization control gb.allowAuthenticatedDescription = grant restricted access to all authenticated users -gb.allowNamedDescription = grant restricted access to named users or teams
\ No newline at end of file +gb.allowNamedDescription = grant restricted access to named users or teams +gb.markdownFailure = Failed to parse Markdown content!
\ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/CommitDiffPage.java b/src/com/gitblit/wicket/pages/CommitDiffPage.java index cee065bb..dbf981b4 100644 --- a/src/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/com/gitblit/wicket/pages/CommitDiffPage.java @@ -31,6 +31,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.PathModel.PathChangeModel;
+import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.DiffUtils;
import com.gitblit.utils.DiffUtils.DiffOutputType;
import com.gitblit.utils.JGitUtils;
@@ -86,26 +87,55 @@ public class CommitDiffPage extends RepositoryPage { setChangeTypeTooltip(changeType, entry.changeType);
item.add(changeType);
+ boolean hasSubmodule = false;
+ String submodulePath = null;
if (entry.isTree()) {
+ // tree
item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path)));
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(new LinkPanel("pathName", "list", entry.path + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils
+ .newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
} else {
+ // blob
item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class,
WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path)));
}
- item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path)));
- item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path)));
- item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path)));
- item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path))
- .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
-
+ // quick links
+ if (entry.isSubmodule()) {
+ // submodule
+ item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path)).setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
+ .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path)).setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path))
+ .setEnabled(hasSubmodule));
+ } else {
+ // tree or blob
+ item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ }
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java index f3b32652..7bc6b41d 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.java +++ b/src/com/gitblit/wicket/pages/CommitPage.java @@ -36,6 +36,7 @@ import com.gitblit.DownloadZipServlet; import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.GitNote;
+import com.gitblit.models.SubmoduleModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.WicketUtils;
@@ -52,7 +53,7 @@ public class CommitPage extends RepositoryPage { Repository r = getRepository();
RevCommit c = getCommit();
-
+
List<String> parents = new ArrayList<String>();
if (c.getParentCount() > 0) {
for (RevCommit parent : c.getParents()) {
@@ -150,28 +151,60 @@ public class CommitPage extends RepositoryPage { WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
setChangeTypeTooltip(changeType, entry.changeType);
item.add(changeType);
+
+ boolean hasSubmodule = false;
+ String submodulePath = null;
if (entry.isTree()) {
+ // tree
item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path)));
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(new LinkPanel("pathName", "list", entry.path + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
} else {
+ // blob
item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class,
WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path)));
}
-
- item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path))
- .setEnabled(!entry.changeType.equals(ChangeType.ADD)
- && !entry.changeType.equals(ChangeType.DELETE)));
- item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path)));
- item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path))
- .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
- item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path))
- .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+
+ // quick links
+ if (entry.isSubmodule()) {
+ // submodule
+ item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path))
+ .setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
+ .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path))
+ .setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(submodulePath, entry.objectId, entry.path))
+ .setEnabled(hasSubmodule));
+ } else {
+ // tree or blob
+ item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)
+ && !entry.changeType.equals(ChangeType.DELETE)));
+ item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ }
WicketUtils.setAlternatingBackground(item, counter);
counter++;
diff --git a/src/com/gitblit/wicket/pages/MarkdownPage.java b/src/com/gitblit/wicket/pages/MarkdownPage.java index 5764235a..e032cbf9 100644 --- a/src/com/gitblit/wicket/pages/MarkdownPage.java +++ b/src/com/gitblit/wicket/pages/MarkdownPage.java @@ -15,6 +15,7 @@ */
package com.gitblit.wicket.pages;
+import java.text.MessageFormat;
import java.text.ParseException;
import org.apache.wicket.PageParameters;
@@ -27,6 +28,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.GitBlit;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
public class MarkdownPage extends RepositoryPage {
@@ -56,8 +58,8 @@ public class MarkdownPage extends RepositoryPage { try {
htmlText = MarkdownUtils.transformMarkdown(markdownText);
} catch (ParseException p) {
- error(p.getMessage());
- htmlText = markdownText;
+ markdownText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", getString("gb.error"), getString("gb.markdownFailure"), markdownText);
+ htmlText = StringUtils.breakLinesForHtml(markdownText);
}
// Add the html to the page
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index 4b5e28d4..6d33a147 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -19,9 +19,12 @@ import java.io.Serializable; import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
@@ -45,6 +48,7 @@ import com.gitblit.Keys; import com.gitblit.PagesServlet;
import com.gitblit.SyndicationServlet;
import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
@@ -62,18 +66,20 @@ public abstract class RepositoryPage extends BasePage { protected final String repositoryName;
protected final String objectId;
-
+
private transient Repository r;
private RepositoryModel m;
+ private Map<String, SubmoduleModel> submodules;
+
private final Map<String, PageRegistration> registeredPages;
public RepositoryPage(PageParameters params) {
super(params);
repositoryName = WicketUtils.getRepositoryName(params);
objectId = WicketUtils.getObject(params);
-
+
if (StringUtils.isEmpty(repositoryName)) {
error(MessageFormat.format(getString("gb.repositoryNotSpecifiedFor"), getPageName()), true);
}
@@ -206,8 +212,85 @@ public abstract class RepositoryPage extends BasePage { error(MessageFormat.format(getString("gb.failedToFindCommit"),
objectId, repositoryName, getPageName()), true);
}
+ getSubmodules(commit);
return commit;
}
+
+ private Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
+ if (submodules == null) {
+ submodules = new HashMap<String, SubmoduleModel>();
+ for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
+ submodules.put(model.path, model);
+ }
+ }
+ return submodules;
+ }
+
+ protected Map<String, SubmoduleModel> getSubmodules() {
+ return submodules;
+ }
+
+ protected SubmoduleModel getSubmodule(String path) {
+ SubmoduleModel model = submodules.get(path);
+ if (model == null) {
+ // undefined submodule?!
+ model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
+ model.hasSubmodule = false;
+ model.gitblitPath = model.name;
+ return model;
+ } else {
+ // extract the repository name from the clone url
+ List<String> patterns = GitBlit.getStrings(Keys.git.submoduleUrlPatterns);
+ String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
+
+ // determine the current path for constructing paths relative
+ // to the current repository
+ String currentPath = "";
+ if (repositoryName.indexOf('/') > -1) {
+ currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1);
+ }
+
+ // try to locate the submodule repository
+ // prefer bare to non-bare names
+ List<String> candidates = new ArrayList<String>();
+
+ // relative
+ candidates.add(currentPath + StringUtils.stripDotGit(submoduleName));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+
+ // relative, no subfolder
+ if (submoduleName.lastIndexOf('/') > -1) {
+ String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
+ candidates.add(currentPath + StringUtils.stripDotGit(name));
+ candidates.add(currentPath + candidates.get(candidates.size() - 1) + ".git");
+ }
+
+ // absolute
+ candidates.add(StringUtils.stripDotGit(submoduleName));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+
+ // absolute, no subfolder
+ if (submoduleName.lastIndexOf('/') > -1) {
+ String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
+ candidates.add(StringUtils.stripDotGit(name));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+ }
+
+ // create a unique, ordered set of candidate paths
+ Set<String> paths = new LinkedHashSet<String>(candidates);
+ for (String candidate : paths) {
+ if (GitBlit.self().hasRepository(candidate)) {
+ model.hasSubmodule = true;
+ model.gitblitPath = candidate;
+ return model;
+ }
+ }
+
+ // we do not have a copy of the submodule, but we need a path
+ model.gitblitPath = candidates.get(0);
+ return model;
+ }
+ }
protected String getShortObjectId(String objectId) {
return objectId.substring(0, 8);
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.java b/src/com/gitblit/wicket/pages/SummaryPage.java index cb507d23..2a624c26 100644 --- a/src/com/gitblit/wicket/pages/SummaryPage.java +++ b/src/com/gitblit/wicket/pages/SummaryPage.java @@ -136,6 +136,7 @@ public class SummaryPage extends RepositoryPage { if (getRepositoryModel().showReadme) {
String htmlText = null;
+ String markdownText = null;
String readme = null;
try {
RevCommit head = JGitUtils.getCommit(r, null);
@@ -158,11 +159,12 @@ public class SummaryPage extends RepositoryPage { }
if (!StringUtils.isEmpty(readme)) {
String [] encodings = GitBlit.getEncodings();
- String markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings);
+ markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings);
htmlText = MarkdownUtils.transformMarkdown(markdownText);
}
} catch (ParseException p) {
- error(p.getMessage());
+ markdownText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", getString("gb.error"), getString("gb.markdownFailure"), markdownText);
+ htmlText = StringUtils.breakLinesForHtml(markdownText);
}
Fragment fragment = new Fragment("readme", "markdownPanel");
fragment.add(new Label("readmeFile", readme));
diff --git a/src/com/gitblit/wicket/pages/TreePage.html b/src/com/gitblit/wicket/pages/TreePage.html index 760294f5..0047ff0d 100644 --- a/src/com/gitblit/wicket/pages/TreePage.html +++ b/src/com/gitblit/wicket/pages/TreePage.html @@ -29,6 +29,13 @@ </tr>
</table>
+ <!-- submodule links -->
+ <wicket:fragment wicket:id="submoduleLinks">
+ <span class="link">
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>
+ </span>
+ </wicket:fragment>
+
<!-- tree links -->
<wicket:fragment wicket:id="treeLinks">
<span class="link">
diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java index 7fc91eee..973634b7 100644 --- a/src/com/gitblit/wicket/pages/TreePage.java +++ b/src/com/gitblit/wicket/pages/TreePage.java @@ -34,6 +34,7 @@ import com.gitblit.DownloadZipServlet; import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.PathModel;
+import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.WicketUtils;
@@ -71,7 +72,7 @@ public class TreePage extends RepositoryPage { if (path.lastIndexOf('/') > -1) {
parentPath = path.substring(0, path.lastIndexOf('/'));
}
- PathModel model = new PathModel("..", parentPath, 0, FileMode.TREE.getBits(), objectId);
+ PathModel model = new PathModel("..", parentPath, 0, FileMode.TREE.getBits(), null, objectId);
model.isParentPath = true;
paths.add(0, model);
}
@@ -118,6 +119,34 @@ public class TreePage extends RepositoryPage { repositoryName, objectId, entry.path)).setVisible(GitBlit
.getBoolean(Keys.web.allowZipDownloads, true)));
item.add(links);
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ String submodulePath;
+ boolean hasSubmodule = false;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(WicketUtils.newImage("pathIcon", "git-orange-16x16.png"));
+ item.add(new Label("pathSize", ""));
+ item.add(new LinkPanel("pathName", "list", entry.name + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
+
+ Fragment links = new Fragment("pathLinks", "submoduleLinks", this);
+ links.add(new BookmarkablePageLink<Void>("view", SummaryPage.class,
+ WicketUtils.newRepositoryParameter(submodulePath)).setEnabled(hasSubmodule));
+ links.add(new BookmarkablePageLink<Void>("tree", TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId,
+ "")).setEnabled(hasSubmodule));
+ links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId,
+ "")).setEnabled(hasSubmodule));
+ links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl,
+ submodulePath, submoduleId, "")).setVisible(GitBlit
+ .getBoolean(Keys.web.allowZipDownloads, true)).setEnabled(hasSubmodule));
+ item.add(links);
} else {
// blob link
item.add(WicketUtils.getFileImage("pathIcon", entry.name));
diff --git a/tests/com/gitblit/tests/StringUtilsTest.java b/tests/com/gitblit/tests/StringUtilsTest.java index 91bfa672..bcf3a99c 100644 --- a/tests/com/gitblit/tests/StringUtilsTest.java +++ b/tests/com/gitblit/tests/StringUtilsTest.java @@ -150,4 +150,11 @@ public class StringUtilsTest { assertFalse(StringUtils.fuzzyMatch("123", "12345"));
assertFalse(StringUtils.fuzzyMatch("AbCdEfHIJ", "abc*hhh"));
}
+
+ @Test
+ public void testGetRepositoryPath() throws Exception {
+ assertEquals("gitblit/gitblit.git", StringUtils.extractRepositoryPath("git://github.com/gitblit/gitblit.git", new String [] { ".*?://github.com/(.*)" }));
+ assertEquals("gitblit.git", StringUtils.extractRepositoryPath("git://github.com/gitblit/gitblit.git", new String [] { ".*?://github.com/[^/].*?/(.*)" }));
+ assertEquals("gitblit.git", StringUtils.extractRepositoryPath("git://github.com/gitblit/gitblit.git"));
+ }
}
|