@@ -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://. |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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 { |
@@ -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; | |||
} | |||
} |
@@ -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 | |||
gb.allowNamedDescription = grant restricted access to named users or teams | |||
gb.markdownFailure = Failed to parse Markdown content! |
@@ -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 | |||
gb.allowNamedDescription = grant restricted access to named users or teams | |||
gb.markdownFailure = Failed to parse Markdown content! |
@@ -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 | |||
gb.allowNamedDescription = grant restricted access to named users or teams | |||
gb.markdownFailure = Failed to parse Markdown content! |
@@ -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 | |||
gb.allowNamedDescription = grant restricted access to named users or teams | |||
gb.markdownFailure = Failed to parse Markdown content! |
@@ -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++; | |||
} |
@@ -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++; |
@@ -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 |
@@ -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); |
@@ -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)); |
@@ -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"> |
@@ -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)); |
@@ -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")); | |||
} | |||
} |