Merge pull request #969 from paulsputer/968-GitLFS-Extract-URI-From-Repository

fix for #968 extracting repository from URI
diff --git a/.classpath b/.classpath
index da27c4d..ccf6a4e 100644
--- a/.classpath
+++ b/.classpath
@@ -41,7 +41,7 @@
 	<classpathentry kind="lib" path="ext/tracwiki-core-1.4.jar" sourcepath="ext/src/tracwiki-core-1.4.jar" />
 	<classpathentry kind="lib" path="ext/mediawiki-core-1.4.jar" sourcepath="ext/src/mediawiki-core-1.4.jar" />
 	<classpathentry kind="lib" path="ext/confluence-core-1.4.jar" sourcepath="ext/src/confluence-core-1.4.jar" />
-	<classpathentry kind="lib" path="ext/org.eclipse.jgit-4.1.0.201509280440-r.jar" />
+	<classpathentry kind="lib" path="ext/org.eclipse.jgit-4.1.1.201511131810-r.jar" sourcepath="ext/src/org.eclipse.jgit-4.1.1.201511131810-r.jar" />
 	<classpathentry kind="lib" path="ext/jsch-0.1.53.jar" sourcepath="ext/src/jsch-0.1.53.jar" />
 	<classpathentry kind="lib" path="ext/JavaEWAH-0.7.9.jar" sourcepath="ext/src/JavaEWAH-0.7.9.jar" />
 	<classpathentry kind="lib" path="ext/httpclient-4.3.6.jar" sourcepath="ext/src/httpclient-4.3.6.jar" />
@@ -49,7 +49,7 @@
 	<classpathentry kind="lib" path="ext/commons-logging-1.1.3.jar" sourcepath="ext/src/commons-logging-1.1.3.jar" />
 	<classpathentry kind="lib" path="ext/commons-codec-1.7.jar" sourcepath="ext/src/commons-codec-1.7.jar" />
 	<classpathentry kind="lib" path="ext/org.eclipse.jdt.annotation-1.1.0.jar" />
-	<classpathentry kind="lib" path="ext/org.eclipse.jgit.http.server-4.1.0.201509280440-r.jar" />
+	<classpathentry kind="lib" path="ext/org.eclipse.jgit.http.server-4.1.1.201511131810-r.jar" sourcepath="ext/src/org.eclipse.jgit.http.server-4.1.1.201511131810-r.jar" />
 	<classpathentry kind="lib" path="ext/bcprov-jdk15on-1.52.jar" sourcepath="ext/src/bcprov-jdk15on-1.52.jar" />
 	<classpathentry kind="lib" path="ext/bcmail-jdk15on-1.52.jar" sourcepath="ext/src/bcmail-jdk15on-1.52.jar" />
 	<classpathentry kind="lib" path="ext/bcpkix-jdk15on-1.52.jar" sourcepath="ext/src/bcpkix-jdk15on-1.52.jar" />
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..e47f177
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+James Moger <james.moger@gitblit.com>	James Moger <james.moger@gmail.com>
+James Moger <james.moger@gitblit.com>	James Moger <jmoger@vas.com>
diff --git a/README.markdown b/README.markdown
index f4b572b..53776f8 100644
--- a/README.markdown
+++ b/README.markdown
@@ -31,27 +31,7 @@
 Contributing

 ------------

 

-GitHub pull requests or Gitblit Tickets are preferred.  Any contributions must be distributed under the terms of the [Apache Software Foundation license, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).

-

-**Workflow**

-

-Gitblit practices the [git-flow][1] branching model.

-

-- **master** is the current stable release + fixes accumulated since release.

-- **develop** is the integration branch for the next major release.

-- **ticket/N** are feature or hotfix branches to be merged to **master** or **develop**, as appropriate.

-

-**Feature Development**

-

-Development of new features is mostly done using [Gitblit Tickets][2] hosted at [dev.gitblit.com][3].  This allows continuous dogfooding and improvement of Gitbit's own issue-tracker and pull-request mechanism.

-

-**Release Planning**

-

-Release planning is mostly done using Gitblit Milestones and Gitblit Tickets hosted at [dev.gitblit.com][3].

-

-**Releasing**

-

-When Gitblit is preparing for a release, a **release-{milestone}** branch will be created, tested, & fixed until it is ready to be merged to **master** and tagged as the next major release.  After the release is tagged, the **release-{milestone}** branch will also be merged back into **develop** and then the release branch will be removed.

+GitHub pull requests are preferred.  Any contributions must be distributed under the terms of the [Apache Software Foundation license, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).

 

 Building Gitblit

 ----------------

@@ -75,6 +55,3 @@
 1. If you are running Ant from an ANSI-capable console, consider setting the `MX_COLOR` environment variable before executing Ant.<pre>set MX_COLOR=true</pre>

 2. The build script will honor your Maven proxy settings.  If you need to fine-tune this, please review the [settings.moxie](http://gitblit.github.io/moxie/settings.html) documentation.

 

-[1]: http://nvie.com/posts/a-successful-git-branching-model

-[2]: http://gitblit.com/tickets_overview.html

-[3]: https://dev.gitblit.com

diff --git a/build.moxie b/build.moxie
index f82b9f8..8eb2bbf 100644
--- a/build.moxie
+++ b/build.moxie
@@ -10,16 +10,16 @@
 description: pure Java Git solution
 groupId: com.gitblit
 artifactId: gitblit
-version: 1.7.0-SNAPSHOT
+version: 1.7.2-SNAPSHOT
 inceptionYear: 2011
 
 # Current stable release
-releaseVersion: 1.6.2
-releaseDate: 2014-10-28
+releaseVersion: 1.7.1
+releaseDate: 2015-11-23
 
 # Project urls
 url: 'http://gitblit.com'
-issuesUrl: 'http://code.google.com/p/gitblit/issues/list'
+issuesUrl: 'https://github.com/gitblit/gitblit'
 socialNetworkUrl: 'https://plus.google.com/114464678392593421684'
 forumUrl: 'http://groups.google.com/group/gitblit'
 mavenUrl: 'http://gitblit.github.io/gitblit-maven'
@@ -106,7 +106,7 @@
   slf4j.version  : 1.7.12
   wicket.version : 1.4.22
   lucene.version : 4.10.4
-  jgit.version   : 4.1.0.201509280440-r
+  jgit.version   : 4.1.1.201511131810-r
   groovy.version : 2.4.4
   bouncycastle.version : 1.52
   selenium.version : 2.28.0
diff --git a/build.xml b/build.xml
index 65fc043..c7dc499 100644
--- a/build.xml
+++ b/build.xml
@@ -553,8 +553,6 @@
 					<page name="release history" out="releases.html">

 						<template src="releasehistory.ftl" data="${releaselog}" />

 					</page>

-					<divider />

-					<page name="roadmap" src="roadmap.mkd" />					

 				</menu>

 				

 				<menu name="downloads">

@@ -585,7 +583,6 @@
 					<link name="Github" src="${project.scmUrl}" />

 					<link name="Issues" src="${project.issuesUrl}" />

 					<link name="Discussion" src="${project.forumUrl}" />

-					<link name="Google+" src="${project.socialNetworkUrl}" />

 					<link name="Twitter" src="https://twitter.com/gitblit" />

 					<link name="Ohloh" src="http://www.ohloh.net/p/gitblit" />

                     <divider />

@@ -593,7 +590,6 @@
 					<link name="Gitblit SSH and Plugin Management asciicast" src="https://asciinema.org/a/9342" />

                     <link name="GitMinutes #29: James Moger on Gitblit" src="http://episodes.gitminutes.com/2014/05/gitminutes-29-james-moger-on-gitblit.html" />

 					<divider />

-					<link name="+JamesMoger" src="https://plus.google.com/+JamesMoger" />

 					<link name="@JamesMoger" src="https://twitter.com/JamesMoger" />

 				</menu>

 				<divider />

@@ -897,7 +893,6 @@
 						<link name="Github" src="${project.scmUrl}" />

 						<link name="Issues" src="${project.issuesUrl}" />

 						<link name="Discussion" src="${project.forumUrl}" />

-						<link name="Google+" src="${project.socialNetworkUrl}" />

 						<link name="Ohloh" src="http://www.ohloh.net/p/gitblit" />

 					</menu>

 				</structure>

diff --git a/gitblit.iml b/gitblit.iml
index 62b1e45..93331b2 100644
--- a/gitblit.iml
+++ b/gitblit.iml
@@ -408,12 +408,14 @@
       </library>
     </orderEntry>
     <orderEntry type="module-library">
-      <library name="org.eclipse.jgit-4.1.0.201509280440-r.jar">
+      <library name="org.eclipse.jgit-4.1.1.201511131810-r.jar">
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/ext/org.eclipse.jgit-4.1.0.201509280440-r.jar!/" />
+          <root url="jar://$MODULE_DIR$/ext/org.eclipse.jgit-4.1.1.201511131810-r.jar!/" />
         </CLASSES>
         <JAVADOC />
-        <SOURCES />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/org.eclipse.jgit-4.1.1.201511131810-r.jar!/" />
+        </SOURCES>
       </library>
     </orderEntry>
     <orderEntry type="module-library">
@@ -492,12 +494,14 @@
       </library>
     </orderEntry>
     <orderEntry type="module-library">
-      <library name="org.eclipse.jgit.http.server-4.1.0.201509280440-r.jar">
+      <library name="org.eclipse.jgit.http.server-4.1.1.201511131810-r.jar">
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/ext/org.eclipse.jgit.http.server-4.1.0.201509280440-r.jar!/" />
+          <root url="jar://$MODULE_DIR$/ext/org.eclipse.jgit.http.server-4.1.1.201511131810-r.jar!/" />
         </CLASSES>
         <JAVADOC />
-        <SOURCES />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/org.eclipse.jgit.http.server-4.1.1.201511131810-r.jar!/" />
+        </SOURCES>
       </library>
     </orderEntry>
     <orderEntry type="module-library">
diff --git a/release.template b/release.template
index 0fd61c5..1322519 100644
--- a/release.template
+++ b/release.template
@@ -60,23 +60,12 @@
 # merge to master
 echo ""
 echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
-echo "Merging release ${project.version} to master"
+echo "Updating build identifier for next release cycle"
 echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
 echo ""
 git checkout master
-git merge --no-ff -m "Merge release ${project.version}" ${project.commitId}
 ant nextPointReleaseCycle
 
-# merge to develop
-echo ""
-echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
-echo "Merging release ${project.version} to develop"
-echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
-echo ""
-git checkout develop
-git merge --no-ff -m "Merge release ${project.version}" ${project.commitId}
-ant nextMinorReleaseCycle
-
 # push Maven repository to origin
 echo ""
 echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
@@ -90,7 +79,7 @@
 # push project branches
 echo ""
 echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
-echo "Pushing master, develop, gh-pages, and tag ${project.tag}"
+echo "Pushing master, gh-pages, and tag ${project.tag}"
 echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
 echo ""
-git push origin master develop gh-pages ${project.tag}
+git push origin master gh-pages ${project.tag}
diff --git a/releases.moxie b/releases.moxie
index f40eb77..f18834b 100644
--- a/releases.moxie
+++ b/releases.moxie
@@ -1,35 +1,53 @@
 #
 # ${project.version} release
 #
-r27: {
+r29: {
     title: ${project.name} ${project.version} released
     id: ${project.version}
     date: ${project.buildDate}
     note: ~
     html: ~
-    text: ''
-          The new gitblit.properties file "includes" defaults.properties which is the original
-          gitblit.properties file. You may continue using your existing gitblit.properties file
-          as before, however, you might find future upgrades simpler by adopting the new "include"
-          design.
-          ''
+    text: ~
     security: ~
     fixes:
     - Fix exception when viewing a ticket with a patchset where the integration branch does not exist (issue-521, ticket-212)
     - Fix exception when deleting a repository using the FileTicketService (issue-522, ticket-213)
-    - Do not inject team repository permissions as explicit user permissoins when editing a user (issue-462, ticket-214)
-    - Whitelist the target link attribute in the XSS filter (ticket-216)
+    - Do not inject team repository permissions as explicit user permissions when editing a user (issue-462, ticket-214)
+    - Whitelist the target link attribute in the XSS filter (ticket-216)    
+    - Strip line breaks from pasted SSH keys (ticket-245)
+    - Fix project sorting (pr-287)
+    - Fix Lucene indexing of tags (pr-291)
+    - Prevent session fixation for external authentication (pr-908)
+    - Encode email subject as UTF-8 (pr-929)
+    - Do not automatically trim passwords (pr-932)
+    - Fix nested repository detection in raw servlet (pr-950, pr-957)
+    - Raw servlet will now assume text/plain for dot files (pr-956)
     changes:
     - Replaced Dagger with Guice (ticket-80)
     - Use release name as root directory in Gitblit GO artifacts (ticket-109)
     - Split gitblit.properties into gitblit.properties & defaults.properties (ticket-110)
     - Show team type in teams page (pr-217, ticket-168)
+    - Relocate the repository Delete button (ticket-225)
+    - Improve diff performance by gracefully limiting large diffs (pr-226)
+    - Add granular settings to disable display of git transport urls (pr-274)
     - Use author date to be consistent with other tools (pr-919)
     additions:
     - Add GitHub Octicons (ticket-106)
     - Support for chain-loading properties files (ticket-110) 
     - Add Priority & Severity fields for tickets (pr-220, ticket-157)
-    - Add Maintenance ticket type (pr-223, ticket-206)    
+    - Add Maintenance ticket type (pr-223, ticket-206)
+    - Add commitdiff option to ignore whitespace (ticket-233)
+    - Add configurable tab length for blob views (ticket-253)
+    - Implement image diffs (pr-229)
+    - Add support for configurable HTTP proxy host/port in PluginManager (pr-235)
+    - Implement collapsed empty folder navigation (pr-241)
+    - Implement hashing to detect usermodel changes and reduce users.conf file I/O (pr-246)
+    - Add support for Kerberos5/GSS authentication to SSH (pr-254)
+    - Allow extraction of additional user metadata in request headers when using external or container authentication (pr-255)
+    - Allow custom host & port specification for advertised SSH urls (pr-268)
+    - Improve logging for fail2ban usage (pr-296)
+    - Initial implementation of Git-LFS (pr-921)
+    - Add "all" repositories parameter to Search page (pr-935)
     dependencyChanges:
     - Guice 4.0 (ticket-80, ticket-219)
     - SLF4j 1.7.12
@@ -37,7 +55,7 @@
     - Freemarker 2.3.22
     - Lucene 4.10.0 (ticket-159)
     - SSHD 1.0.0
-    - JGit 4.0.2
+    - JGit 4.1.1
     - Groovy 2.4.4
     - Wicket 1.4.22
     - BouncyCastle 1.52
@@ -45,6 +63,30 @@
     - Jetty 9.2.13
     settings:
     - { name: web.displayUserPanel, defaultValue: 'true' }
+    - { name: web.tabLength, defaultValue: 4 }
+    - { name: web.avatarClass, defaultValue: '' }
+    - { name: web.showHttpServletUrls, defaultValue: 'true' }
+    - { name: web.showGitDaemonUrls, defaultValue: 'true' }
+    - { name: web.showSshDaemonUrls, defaultValue: 'true' }
+    - { name: web.advertiseAccessPermissionForOtherUrls, defaultValue: 'false' }
+    - { name: web.maxDiffLinesPerFile, defaultValue: '4000' }
+    - { name: web.maxDiffLines, defaultValue: '20000' }
+    - { name: ssh.advertisedHost, defaultValue: '' }
+    - { name: ssh.advertisedPort, defaultValue: '' }
+    - { name: git.sshWithKrb5, defaultValue: '' }
+    - { name: git.sshKrb5Keytab, defaultValue: '' }
+    - { name: git.sshKrb5ServicePrincipalName, defaultValue: '' }
+    - { name: git.sshKrb5StripDomain, defaultValue: 'true' }
+    - { name: filestore.storageFolder, defaultValue: '${baseFolder}/lfs' }
+    - { name: filestore.maxUploadSize, defaultValue: '-1' }
+    - { name: plugins.httpProxyHost, defaultValue: '' }
+    - { name: plugins.httpProxyPort, defaultValue: '' }
+    - { name: plugins.httpProxyAuthorization, defaultValue: '' }
+    - { name: realm.container.autoAccounts.displayName, defaultValue: '' }
+    - { name: realm.container.autoAccounts.emailAddress, defaultValue: '' }
+    - { name: realm.container.autoAccounts.locale, defaultValue: '' }
+    - { name: realm.container.autoAccounts.adminRole, defaultValue: '' }
+    
     contributors:
     - James Moger
     - David Ostrovsky
@@ -53,6 +95,242 @@
     - Paul Martin
     - razzard
     - Alexander Zabluda
+    - Marcin Cieślak
+    - Rainer W
+    - Vitaliy Filippov
+    - willyann
+    - enrico204
+    - mrjoel
+    - Fabrice Bacchella
+    - Milos Cubrilo
+    - Thomas Wolf
+    - Morten Bøgeskov
+    - Steven Oliver
+    - Dariusz Bywalec
+    - Jan Šmucr
+    -paladox
+}
+
+#
+# 1.7.1 release
+#
+r28: {
+    title: Gitblit 1.7.1 released
+    id: 1.7.1
+    date: 2015-11-23
+    note: This is a re-build of 1.7.0 with a fix for failed WAR deployments.
+    html: ~
+    text: ~
+    security: ~
+    fixes:
+    - Fix exception when viewing a ticket with a patchset where the integration branch does not exist (issue-521, ticket-212)
+    - Fix exception when deleting a repository using the FileTicketService (issue-522, ticket-213)
+    - Do not inject team repository permissions as explicit user permissions when editing a user (issue-462, ticket-214)
+    - Whitelist the target link attribute in the XSS filter (ticket-216)    
+    - Strip line breaks from pasted SSH keys (ticket-245)
+    - Fix project sorting (pr-287)
+    - Fix Lucene indexing of tags (pr-291)
+    - Prevent session fixation for external authentication (pr-908)
+    - Encode email subject as UTF-8 (pr-929)
+    - Do not automatically trim passwords (pr-932)
+    - Fix nested repository detection in raw servlet (pr-950)
+    changes:
+    - Replaced Dagger with Guice (ticket-80)
+    - Use release name as root directory in Gitblit GO artifacts (ticket-109)
+    - Split gitblit.properties into gitblit.properties & defaults.properties (ticket-110)
+    - Show team type in teams page (pr-217, ticket-168)
+    - Relocate the repository Delete button (ticket-225)
+    - Improve diff performance by gracefully limiting large diffs (pr-226)
+    - Add granular settings to disable display of git transport urls (pr-274)
+    - Use author date to be consistent with other tools (pr-919)
+    additions:
+    - Add GitHub Octicons (ticket-106)
+    - Support for chain-loading properties files (ticket-110) 
+    - Add Priority & Severity fields for tickets (pr-220, ticket-157)
+    - Add Maintenance ticket type (pr-223, ticket-206)
+    - Add commitdiff option to ignore whitespace (ticket-233)
+    - Add configurable tab length for blob views (ticket-253)
+    - Implement image diffs (pr-229)
+    - Add support for configurable HTTP proxy host/port in PluginManager (pr-235)
+    - Implement collapsed empty folder navigation (pr-241)
+    - Implement hashing to detect usermodel changes and reduce users.conf file I/O (pr-246)
+    - Add support for Kerberos5/GSS authentication to SSH (pr-254)
+    - Allow extraction of additional user metadata in request headers when using external or container authentication (pr-255)
+    - Allow custom host & port specification for advertised SSH urls (pr-268)
+    - Improve logging for fail2ban usage (pr-296)
+    - Initial implementation of Git-LFS (pr-921)
+    - Add "all" repositories parameter to Search page (pr-935)
+    dependencyChanges:
+    - Guice 4.0 (ticket-80, ticket-219)
+    - SLF4j 1.7.12
+    - gson 2.3.1
+    - Freemarker 2.3.22
+    - Lucene 4.10.0 (ticket-159)
+    - SSHD 1.0.0
+    - JGit 4.1.1
+    - Groovy 2.4.4
+    - Wicket 1.4.22
+    - BouncyCastle 1.52
+    - Pegdown 1.5.0
+    - Jetty 9.2.13
+    settings:
+    - { name: web.displayUserPanel, defaultValue: 'true' }
+    - { name: web.tabLength, defaultValue: 4 }
+    - { name: web.avatarClass, defaultValue: '' }
+    - { name: web.showHttpServletUrls, defaultValue: 'true' }
+    - { name: web.showGitDaemonUrls, defaultValue: 'true' }
+    - { name: web.showSshDaemonUrls, defaultValue: 'true' }
+    - { name: web.advertiseAccessPermissionForOtherUrls, defaultValue: 'false' }
+    - { name: web.maxDiffLinesPerFile, defaultValue: '4000' }
+    - { name: web.maxDiffLines, defaultValue: '20000' }
+    - { name: ssh.advertisedHost, defaultValue: '' }
+    - { name: ssh.advertisedPort, defaultValue: '' }
+    - { name: git.sshWithKrb5, defaultValue: '' }
+    - { name: git.sshKrb5Keytab, defaultValue: '' }
+    - { name: git.sshKrb5ServicePrincipalName, defaultValue: '' }
+    - { name: git.sshKrb5StripDomain, defaultValue: 'true' }
+    - { name: filestore.storageFolder, defaultValue: '${baseFolder}/lfs' }
+    - { name: filestore.maxUploadSize, defaultValue: '-1' }
+    - { name: plugins.httpProxyHost, defaultValue: '' }
+    - { name: plugins.httpProxyPort, defaultValue: '' }
+    - { name: plugins.httpProxyAuthorization, defaultValue: '' }
+    - { name: realm.container.autoAccounts.displayName, defaultValue: '' }
+    - { name: realm.container.autoAccounts.emailAddress, defaultValue: '' }
+    - { name: realm.container.autoAccounts.locale, defaultValue: '' }
+    - { name: realm.container.autoAccounts.adminRole, defaultValue: '' }
+    
+    contributors:
+    - James Moger
+    - David Ostrovsky
+    - Alex Lewis
+    - Florian Zschocke
+    - Paul Martin
+    - razzard
+    - Alexander Zabluda
+    - Marcin Cieślak
+    - Rainer W
+    - Vitaliy Filippov
+    - willyann
+    - enrico204
+    - mrjoel
+    - Fabrice Bacchella
+    - Milos Cubrilo
+    - Thomas Wolf
+    - Morten Bøgeskov
+    - Steven Oliver
+    - Dariusz Bywalec
+    - Jan Šmucr
+}
+
+#
+# 1.7.0 release
+#
+r27: {
+    title: Gitblit 1.7.0 released
+    id: 1.7.0
+    date: 2015-11-22
+    note: ~
+    html: ~
+    text: ~
+    security: ~
+    fixes:
+    - Fix exception when viewing a ticket with a patchset where the integration branch does not exist (issue-521, ticket-212)
+    - Fix exception when deleting a repository using the FileTicketService (issue-522, ticket-213)
+    - Do not inject team repository permissions as explicit user permissions when editing a user (issue-462, ticket-214)
+    - Whitelist the target link attribute in the XSS filter (ticket-216)    
+    - Strip line breaks from pasted SSH keys (ticket-245)
+    - Fix project sorting (pr-287)
+    - Fix Lucene indexing of tags (pr-291)
+    - Prevent session fixation for external authentication (pr-908)
+    - Encode email subject as UTF-8 (pr-929)
+    - Do not automatically trim passwords (pr-932)
+    - Fix nested repository detection in raw servlet (pr-950)
+    changes:
+    - Replaced Dagger with Guice (ticket-80)
+    - Use release name as root directory in Gitblit GO artifacts (ticket-109)
+    - Split gitblit.properties into gitblit.properties & defaults.properties (ticket-110)
+    - Show team type in teams page (pr-217, ticket-168)
+    - Relocate the repository Delete button (ticket-225)
+    - Improve diff performance by gracefully limiting large diffs (pr-226)
+    - Add granular settings to disable display of git transport urls (pr-274)
+    - Use author date to be consistent with other tools (pr-919)
+    additions:
+    - Add GitHub Octicons (ticket-106)
+    - Support for chain-loading properties files (ticket-110) 
+    - Add Priority & Severity fields for tickets (pr-220, ticket-157)
+    - Add Maintenance ticket type (pr-223, ticket-206)
+    - Add commitdiff option to ignore whitespace (ticket-233)
+    - Add configurable tab length for blob views (ticket-253)
+    - Implement image diffs (pr-229)
+    - Add support for configurable HTTP proxy host/port in PluginManager (pr-235)
+    - Implement collapsed empty folder navigation (pr-241)
+    - Implement hashing to detect usermodel changes and reduce users.conf file I/O (pr-246)
+    - Add support for Kerberos5/GSS authentication to SSH (pr-254)
+    - Allow extraction of additional user metadata in request headers when using external or container authentication (pr-255)
+    - Allow custom host & port specification for advertised SSH urls (pr-268)
+    - Improve logging for fail2ban usage (pr-296)
+    - Initial implementation of Git-LFS (pr-921)
+    - Add "all" repositories parameter to Search page (pr-935)
+    dependencyChanges:
+    - Guice 4.0 (ticket-80, ticket-219)
+    - SLF4j 1.7.12
+    - gson 2.3.1
+    - Freemarker 2.3.22
+    - Lucene 4.10.0 (ticket-159)
+    - SSHD 1.0.0
+    - JGit 4.1.1
+    - Groovy 2.4.4
+    - Wicket 1.4.22
+    - BouncyCastle 1.52
+    - Pegdown 1.5.0
+    - Jetty 9.2.13
+    settings:
+    - { name: web.displayUserPanel, defaultValue: 'true' }
+    - { name: web.tabLength, defaultValue: 4 }
+    - { name: web.avatarClass, defaultValue: '' }
+    - { name: web.showHttpServletUrls, defaultValue: 'true' }
+    - { name: web.showGitDaemonUrls, defaultValue: 'true' }
+    - { name: web.showSshDaemonUrls, defaultValue: 'true' }
+    - { name: web.advertiseAccessPermissionForOtherUrls, defaultValue: 'false' }
+    - { name: web.maxDiffLinesPerFile, defaultValue: '4000' }
+    - { name: web.maxDiffLines, defaultValue: '20000' }
+    - { name: ssh.advertisedHost, defaultValue: '' }
+    - { name: ssh.advertisedPort, defaultValue: '' }
+    - { name: git.sshWithKrb5, defaultValue: '' }
+    - { name: git.sshKrb5Keytab, defaultValue: '' }
+    - { name: git.sshKrb5ServicePrincipalName, defaultValue: '' }
+    - { name: git.sshKrb5StripDomain, defaultValue: 'true' }
+    - { name: filestore.storageFolder, defaultValue: '${baseFolder}/lfs' }
+    - { name: filestore.maxUploadSize, defaultValue: '-1' }
+    - { name: plugins.httpProxyHost, defaultValue: '' }
+    - { name: plugins.httpProxyPort, defaultValue: '' }
+    - { name: plugins.httpProxyAuthorization, defaultValue: '' }
+    - { name: realm.container.autoAccounts.displayName, defaultValue: '' }
+    - { name: realm.container.autoAccounts.emailAddress, defaultValue: '' }
+    - { name: realm.container.autoAccounts.locale, defaultValue: '' }
+    - { name: realm.container.autoAccounts.adminRole, defaultValue: '' }
+    
+    contributors:
+    - James Moger
+    - David Ostrovsky
+    - Alex Lewis
+    - Florian Zschocke
+    - Paul Martin
+    - razzard
+    - Alexander Zabluda
+    - Marcin Cieślak
+    - Rainer W
+    - Vitaliy Filippov
+    - willyann
+    - enrico204
+    - mrjoel
+    - Fabrice Bacchella
+    - Milos Cubrilo
+    - Thomas Wolf
+    - Morten Bøgeskov
+    - Steven Oliver
+    - Dariusz Bywalec
+    - Jan Šmucr
 }
 
 #
@@ -1613,6 +1891,6 @@
 	- James Moger
 }
 
-snapshot: &r27
-release: &r26
-releases: &r[1..26]
+snapshot: &r29
+release: &r28
+releases: &r[1..28]
diff --git a/src/main/java/com/gitblit/manager/FederationManager.java b/src/main/java/com/gitblit/manager/FederationManager.java
index f009c1c..8f68733 100644
--- a/src/main/java/com/gitblit/manager/FederationManager.java
+++ b/src/main/java/com/gitblit/manager/FederationManager.java
@@ -367,6 +367,10 @@
 							&& file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT);
 				}
 			});
+			if (files == null) {
+				return list;
+			}
+				
 			for (File file : files) {
 				String json = com.gitblit.utils.FileUtils.readContent(file, null);
 				FederationProposal proposal = JsonUtils.fromJsonString(json,
diff --git a/src/main/java/com/gitblit/manager/FilestoreManager.java b/src/main/java/com/gitblit/manager/FilestoreManager.java
index 33672e4..fe65e21 100644
--- a/src/main/java/com/gitblit/manager/FilestoreManager.java
+++ b/src/main/java/com/gitblit/manager/FilestoreManager.java
@@ -15,6 +15,7 @@
  */
 package com.gitblit.manager;
 
+import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -38,15 +39,13 @@
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
-import org.bouncycastle.util.io.StreamOverflowException;
-import org.eclipse.jetty.io.EofException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
-import com.gitblit.models.FilestoreModel.Status;
 import com.gitblit.models.FilestoreModel;
+import com.gitblit.models.FilestoreModel.Status;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
@@ -62,7 +61,7 @@
  * FilestoreManager handles files uploaded via:
  * 	+ git-lfs
  *  + ticket attachment (TBD)
- * 
+ *
  * Files are stored using their SHA256 hash (as per git-lfs)
  * If the same file is uploaded through different repositories no additional space is used
  * Access is controlled through the current repository permissions.
@@ -76,47 +75,49 @@
 public class FilestoreManager implements IFilestoreManager {
 
 	private final Logger logger = LoggerFactory.getLogger(getClass());
-	
+
 	private final IRuntimeManager runtimeManager;
-	
+
 	private final IStoredSettings settings;
-	
+
 	public static final int UNDEFINED_SIZE = -1;
-	
+
 	private static final String METAFILE = "filestore.json";
-	
+
 	private static final String METAFILE_TMP = "filestore.json.tmp";
-	
+
 	protected static final Type METAFILE_TYPE = new TypeToken<Collection<FilestoreModel>>() {}.getType();
-	
+
 	private Map<String, FilestoreModel > fileCache = new ConcurrentHashMap<String, FilestoreModel>();
-	
-	
+
+
 	@Inject
 	FilestoreManager(
 			IRuntimeManager runtimeManager) {
 		this.runtimeManager = runtimeManager;
 		this.settings = runtimeManager.getSettings();
 	}
-	
+
 	@Override
 	public IManager start() {
 
-		//Try to load any existing metadata
-		File metadata = new File(getStorageFolder(), METAFILE);
-		
+		// Try to load any existing metadata
+		File dir = getStorageFolder();
+		dir.mkdirs();
+		File metadata = new File(dir, METAFILE);
+
 		if (metadata.exists()) {
 			Collection<FilestoreModel> items = null;
-			
+
 			Gson gson = gson();
 			try (FileReader file = new FileReader(metadata)) {
 				items = gson.fromJson(file, METAFILE_TYPE);
 				file.close();
-				
+
 			} catch (IOException e) {
 				e.printStackTrace();
 			}
-			
+
 			for(Iterator<FilestoreModel> itr = items.iterator(); itr.hasNext(); ) {
 			    FilestoreModel model = itr.next();
 			    fileCache.put(model.oid, model);
@@ -128,7 +129,7 @@
 		{
 			logger.info("No filestore metadata file found");
 		}
-		
+
 		return this;
 	}
 
@@ -143,7 +144,7 @@
 		//NOTE: Assuming SHA256 support only as per git-lfs
 		return Pattern.matches("[a-fA-F0-9]{64}", oid);
 	}
-	
+
 	@Override
 	public FilestoreModel.Status addObject(String oid, long size, UserModel user, RepositoryModel repo) {
 
@@ -155,32 +156,32 @@
 				return Status.Error_Unauthorized;
 			}
 		}
-		
+
 		//Handle object details
 		if (!isValidOid(oid)) { return Status.Error_Invalid_Oid; }
-		
+
 		if (fileCache.containsKey(oid)) {
 			FilestoreModel item = fileCache.get(oid);
-			
+
 			if (!item.isInErrorState() && (size != UNDEFINED_SIZE) && (item.getSize() != size)) {
 				return Status.Error_Size_Mismatch;
 			}
-			
+
 			item.addRepository(repo.name);
-			
+
 			if (item.isInErrorState()) {
 				item.reset(user, size);
 			}
 		} else {
-			
+
 			if (size  < 0) {return Status.Error_Invalid_Size; }
 			if ((getMaxUploadSize() != UNDEFINED_SIZE) && (size > getMaxUploadSize())) { return Status.Error_Exceeds_Size_Limit; }
-			
-			FilestoreModel model = new FilestoreModel(oid, size, user, repo.name); 
+
+			FilestoreModel model = new FilestoreModel(oid, size, user, repo.name);
 			fileCache.put(oid, model);
 			saveFilestoreModel(model);
 		}
-		
+
 		return fileCache.get(oid).getStatus();
 	}
 
@@ -188,14 +189,14 @@
 	public FilestoreModel.Status uploadBlob(String oid, long size, UserModel user, RepositoryModel repo, InputStream streamIn) {
 
 		//Access control and object logic
-		Status state = addObject(oid, size, user, repo); 
-		
-		if (state != Status.Upload_Pending) { 
+		Status state = addObject(oid, size, user, repo);
+
+		if (state != Status.Upload_Pending) {
 			return state;
 		}
-		
+
 		FilestoreModel model = fileCache.get(oid);
-		
+
 		if (!model.actionUpload(user)) {
 			return Status.Upload_In_Progress;
 		} else {
@@ -205,55 +206,55 @@
 			try {
 				file.getParentFile().mkdirs();
 				file.createNewFile();
-					
+
 				try (FileOutputStream streamOut = new FileOutputStream(file)) {
-					
+
 					actualSize = IOUtils.copyLarge(streamIn, streamOut);
-					
+
 					streamOut.flush();
 					streamOut.close();
-					
+
 					if (model.getSize() != actualSize) {
 						model.setStatus(Status.Error_Size_Mismatch, user);
-						
-						logger.warn(MessageFormat.format("Failed to upload blob {0} due to size mismatch, expected {1} got {2}", 
+
+						logger.warn(MessageFormat.format("Failed to upload blob {0} due to size mismatch, expected {1} got {2}",
 								oid, model.getSize(), actualSize));
 					} else {
 						String actualOid = "";
-						
+
 						try (FileInputStream fileForHash = new FileInputStream(file)) {
 							actualOid = DigestUtils.sha256Hex(fileForHash);
 							fileForHash.close();
 						}
-						
+
 						if (oid.equalsIgnoreCase(actualOid)) {
 							model.setStatus(Status.Available, user);
 						} else {
 							model.setStatus(Status.Error_Hash_Mismatch, user);
-							
+
 							logger.warn(MessageFormat.format("Failed to upload blob {0} due to hash mismatch, got {1}", oid, actualOid));
 						}
 					}
 				}
 			} catch (Exception e) {
-				
+
 				model.setStatus(Status.Error_Unknown, user);
 				logger.warn(MessageFormat.format("Failed to upload blob {0}", oid), e);
 			} finally {
 				saveFilestoreModel(model);
 			}
-			
+
 			if (model.isInErrorState()) {
 				file.delete();
 				model.removeRepository(repo.name);
 			}
 		}
-		
+
 		return model.getStatus();
 	}
-	
+
 	private FilestoreModel.Status canGetObject(String oid, UserModel user, RepositoryModel repo) {
-	
+
 		//Access Control
 		if (!user.canView(repo)) {
 			if (user == UserModel.ANONYMOUS) {
@@ -264,53 +265,53 @@
 		}
 
 		//Object Logic
-		if (!isValidOid(oid)) { 
+		if (!isValidOid(oid)) {
 			return Status.Error_Invalid_Oid;
 		}
-		
-		if (!fileCache.containsKey(oid)) { 
+
+		if (!fileCache.containsKey(oid)) {
 			return Status.Unavailable;
 		}
-		
+
 		FilestoreModel item = fileCache.get(oid);
-		
+
 		if (item.getStatus() == Status.Available) {
 			return Status.Available;
 		}
-		
+
 		return Status.Unavailable;
 	}
-	
+
 	@Override
 	public FilestoreModel getObject(String oid, UserModel user, RepositoryModel repo) {
-		
+
 		if (canGetObject(oid, user, repo) == Status.Available) {
 			return fileCache.get(oid);
 		}
-		
+
 		return null;
 	}
 
 	@Override
 	public FilestoreModel.Status downloadBlob(String oid, UserModel user, RepositoryModel repo, OutputStream streamOut) {
-		
+
 		//Access control and object logic
 		Status status = canGetObject(oid, user, repo);
-				
-		if (status != Status.Available) { 
+
+		if (status != Status.Available) {
 			return status;
 		}
-				
+
 		FilestoreModel item = fileCache.get(oid);
-		
+
 		if (streamOut != null) {
 			try (FileInputStream streamIn = new FileInputStream(getStoragePath(oid))) {
-				
+
 				IOUtils.copyLarge(streamIn, streamOut);
-				
+
 				streamOut.flush();
 				streamIn.close();
-			} catch (EofException e) {
+			} catch (EOFException e) {
 				logger.error(MessageFormat.format("Client aborted connection for {0}", oid), e);
 				return Status.Error_Unexpected_Stream_End;
 			} catch (Exception e) {
@@ -318,7 +319,7 @@
 				return Status.Error_Unknown;
 			}
 		}
-		
+
 		return item.getStatus();
 	}
 
@@ -331,7 +332,7 @@
 	public File getStorageFolder() {
 		return runtimeManager.getFileOrFolder(Keys.filestore.storageFolder, "${baseFolder}/lfs");
 	}
-	
+
 	@Override
 	public File getStoragePath(String oid) {
 		 return new File(getStorageFolder(), oid.substring(0, 2).concat("/").concat(oid.substring(2)));
@@ -341,41 +342,41 @@
 	public long getMaxUploadSize() {
 		return settings.getLong(Keys.filestore.maxUploadSize, -1);
 	}
-	
+
 	@Override
 	public long getFilestoreUsedByteCount() {
 		Iterator<FilestoreModel> iterator = fileCache.values().iterator();
 		long total = 0;
-		
+
 		while (iterator.hasNext()) {
-			
+
 			FilestoreModel item = iterator.next();
 			if (item.getStatus() == Status.Available) {
 				total += item.getSize();
 			}
 		}
-		
+
 		return total;
 	}
-	
+
 	@Override
 	public long getFilestoreAvailableByteCount() {
-		
+
 		try {
 			return Files.getFileStore(getStorageFolder().toPath()).getUsableSpace();
 		} catch (IOException e) {
 			logger.error(MessageFormat.format("Failed to retrive available space in Filestore {0}", e));
 		}
-		
+
 		return UNDEFINED_SIZE;
 	};
-	
+
 	private synchronized void saveFilestoreModel(FilestoreModel model) {
-		
+
 		File metaFile = new File(getStorageFolder(), METAFILE);
 		File metaFileTmp = new File(getStorageFolder(), METAFILE_TMP);
 		boolean isNewFile = false;
-		
+
 		try {
 			if (!metaFile.exists()) {
 				metaFile.getParentFile().mkdirs();
@@ -383,33 +384,33 @@
 				isNewFile = true;
 			}
 			FileUtils.copyFile(metaFile, metaFileTmp);
-			
+
 		} catch (IOException e) {
 			logger.error("Writing filestore model to file {0}, {1}", METAFILE, e);
 		}
-		
+
 		try (RandomAccessFile fs = new RandomAccessFile(metaFileTmp, "rw")) {
-		
+
 			if (isNewFile) {
 				fs.writeBytes("[");
 			} else {
 				fs.seek(fs.length() - 1);
 				fs.writeBytes(",");
 			}
-			
+
 			fs.writeBytes(gson().toJson(model));
 			fs.writeBytes("]");
-			
+
 			fs.close();
-			
+
 		} catch (IOException e) {
 			logger.error("Writing filestore model to file {0}, {1}", METAFILE_TMP, e);
 		}
-		
+
 		try {
 			if (metaFileTmp.exists()) {
 				FileUtils.copyFile(metaFileTmp, metaFile);
-				
+
 				metaFileTmp.delete();
 			} else {
 				logger.error("Writing filestore model to file {0}", METAFILE);
@@ -419,14 +420,15 @@
 			logger.error("Writing filestore model to file {0}, {1}", METAFILE, e);
 		}
 	}
-	
+
 	/*
 	 * Intended for testing purposes only
 	 */
+	@Override
 	public void clearFilestoreCache() {
 		fileCache.clear();
 	}
-	
+
 	private static Gson gson(ExclusionStrategy... strategies) {
 		GsonBuilder builder = new GsonBuilder();
 		builder.registerTypeAdapter(Date.class, new GmtDateTypeAdapter());
@@ -435,5 +437,5 @@
 		}
 		return builder.create();
 	}
-	
+
 }
diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java
index 027ba23..e2e4de6 100644
--- a/src/main/java/com/gitblit/manager/RepositoryManager.java
+++ b/src/main/java/com/gitblit/manager/RepositoryManager.java
@@ -1113,9 +1113,16 @@
 			// find the root, cached
 			String key = getRepositoryKey(repository);
 			RepositoryModel model = repositoryListCache.get(key);
+			if (model == null) {
+				return null;
+			}
+
 			while (model.originRepository != null) {
 				String originKey = getRepositoryKey(model.originRepository);
 				model = repositoryListCache.get(originKey);
+				if (model == null) {
+					return null;
+				}
 			}
 			ForkModel root = getForkModelFromCache(model.name);
 			return root;
diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java
index fb8f6b9..750da79 100644
--- a/src/main/java/com/gitblit/servlet/GitblitContext.java
+++ b/src/main/java/com/gitblit/servlet/GitblitContext.java
@@ -24,6 +24,7 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;
@@ -455,7 +456,12 @@
 	}
 
 	protected void extractResources(ServletContext context, String path, File toDir) {
-		for (String resource : context.getResourcePaths(path)) {
+		Set<String> resources = context.getResourcePaths(path);
+		if (resources == null) {
+			logger.warn("There are no WAR resources to extract from {}", path);
+			return;
+		}
+		for (String resource : resources) {
 			// extract the resource to the directory if it does not exist
 			File f = new File(toDir, resource.substring(path.length()));
 			if (!f.exists()) {
diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java
index 1d2724b..81793bc 100644
--- a/src/main/java/com/gitblit/servlet/RawServlet.java
+++ b/src/main/java/com/gitblit/servlet/RawServlet.java
@@ -166,23 +166,14 @@
 		}
 
 		// determine repository and resource from url
-		String repository = "";
+		String repository = path;
 		Repository r = null;
-		int offset = 0;
-		while (r == null) {
-			int slash = path.indexOf('/', offset);
-			if (slash == -1) {
-				repository = path;
-			} else {
-				repository = path.substring(0, slash);
-			}
-			offset = ( slash + 1 );
+		int terminator = repository.length();
+		do {
+			repository = repository.substring(0, terminator);
 			r = repositoryManager.getRepository(repository, false);
-			if (repository.equals(path)) {
-				// either only repository in url or no repository found
-				break;
-			}
-		}
+			terminator = repository.lastIndexOf('/');
+		} while (r == null && terminator > -1 );
 
 		ServletContext context = request.getSession().getServletContext();
 
@@ -242,7 +233,7 @@
 				try {
 
 					String ext = StringUtils.getFileExtension(file).toLowerCase();
-					String contentType = quickContentTypes.get(ext);
+					String contentType = file.charAt(0) == '.' ? "text/plain" : quickContentTypes.get(ext);
 
 					if (contentType == null) {
 						List<String> exts = runtimeManager.getSettings().getStrings(Keys.web.prettyPrintExtensions);
diff --git a/src/main/java/com/gitblit/utils/FileUtils.java b/src/main/java/com/gitblit/utils/FileUtils.java
index e7f0104..ad2509d 100644
--- a/src/main/java/com/gitblit/utils/FileUtils.java
+++ b/src/main/java/com/gitblit/utils/FileUtils.java
@@ -140,9 +140,10 @@
 	public static String readContent(File file, String lineEnding) {

 		StringBuilder sb = new StringBuilder();

 		InputStreamReader is = null;

+		BufferedReader reader = null;

 		try {

 			is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));

-			BufferedReader reader = new BufferedReader(is);

+			reader = new BufferedReader(is);

 			String line = null;

 			while ((line = reader.readLine()) != null) {

 				sb.append(line);

@@ -154,6 +155,14 @@
 			System.err.println("Failed to read content of " + file.getAbsolutePath());

 			t.printStackTrace();

 		} finally {

+			if (reader != null){

+				try {

+					reader.close();

+				} catch (IOException ioe) {

+					System.err.println("Failed to close file " + file.getAbsolutePath());

+					ioe.printStackTrace();

+				}

+			}

 			if (is != null) {

 				try {

 					is.close();

diff --git a/src/main/java/com/gitblit/wicket/pages/FilestorePage.java b/src/main/java/com/gitblit/wicket/pages/FilestorePage.java
index 5f103ed..97d5f25 100644
--- a/src/main/java/com/gitblit/wicket/pages/FilestorePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/FilestorePage.java
@@ -29,51 +29,41 @@
 import org.apache.wicket.markup.repeater.data.ListDataProvider;
 
 import com.gitblit.Constants;
-import com.gitblit.Keys;
 import com.gitblit.models.FilestoreModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.wicket.FilestoreUI;
-import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.RequiresAdminRole;
 import com.gitblit.wicket.WicketUtils;
 
 /**
  * Page to display the current status of the filestore.
- * Certain errors also displayed to aid in fault finding  
+ * Certain errors also displayed to aid in fault finding
  *
  * @author Paul Martin
- *
- *
  */
+@RequiresAdminRole
 public class FilestorePage extends RootPage {
 
 	public FilestorePage() {
 		super();
 		setupPage("", "");
-		// check to see if we should display a login message
-		boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, true);
-		if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
-			String messageSource = app().settings().getString(Keys.web.loginMessage, "gitblit");
-			return;
-		}
-		
+
 		final List<FilestoreModel> files = app().filestore().getAllObjects();
 		final long nBytesUsed = app().filestore().getFilestoreUsedByteCount();
 		final long nBytesAvailable = app().filestore().getFilestoreAvailableByteCount();
-		
-		// Load the markdown welcome message
-		String messageSource = app().settings().getString(Keys.web.repositoriesMessage, "gitblit");
-		String message = MessageFormat.format(getString("gb.filestoreStats"), files.size(), 
-				FileUtils.byteCountToDisplaySize(nBytesUsed), FileUtils.byteCountToDisplaySize(nBytesAvailable) ); 
+
+		String message = MessageFormat.format(getString("gb.filestoreStats"), files.size(),
+				FileUtils.byteCountToDisplaySize(nBytesUsed), FileUtils.byteCountToDisplaySize(nBytesAvailable) );
 
 		Component repositoriesMessage = new Label("repositoriesMessage", message)
 				.setEscapeModelStrings(false).setVisible(message.length() > 0);
-		
+
 		add(repositoriesMessage);
-		
+
 		BookmarkablePageLink<Void> helpLink = new BookmarkablePageLink<Void>("filestoreHelp", FilestoreUsage.class);
 		helpLink.add(new Label("helpMessage", getString("gb.filestoreHelp")));
 		add(helpLink);
-		
+
 
 		DataView<FilestoreModel> filesView = new DataView<FilestoreModel>("fileRow",
 				new ListDataProvider<FilestoreModel>(files)) {
@@ -89,26 +79,26 @@
 			@Override
 			public void populateItem(final Item<FilestoreModel> item) {
 				final FilestoreModel entry = item.getModelObject();
-				
+
 				DateFormat dateFormater = new SimpleDateFormat(Constants.ISO8601);
-				
+
 				UserModel user = app().users().getUserModel(entry.getChangedBy());
 				user = user == null ? UserModel.ANONYMOUS : user;
-				
+
 				Label icon = FilestoreUI.getStatusIcon("status", entry);
 				item.add(icon);
 				item.add(new Label("on", dateFormater.format(entry.getChangedOn())));
 				item.add(new Label("by", user.getDisplayName()));
-				
+
 				item.add(new Label("oid", entry.oid));
-				item.add(new Label("size", FileUtils.byteCountToDisplaySize(entry.getSize())));				
-				
+				item.add(new Label("size", FileUtils.byteCountToDisplaySize(entry.getSize())));
+
 				WicketUtils.setAlternatingBackground(item, counter);
 				counter++;
 			}
 
 		};
-		
+
 		add(filesView);
 	}
 }
diff --git a/src/main/java/com/gitblit/wicket/pages/ForksPage.java b/src/main/java/com/gitblit/wicket/pages/ForksPage.java
index 93fc9fa..045f5f7 100644
--- a/src/main/java/com/gitblit/wicket/pages/ForksPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ForksPage.java
@@ -136,6 +136,9 @@
 

 	protected List<FlatFork> flatten(ForkModel node, int level) {

 		List<FlatFork> list = new ArrayList<FlatFork>();

+		if (node == null) {

+			return list;

+		}

 		list.add(new FlatFork(node.repository, level));

 		if (!node.isLeaf()) {

 			for (ForkModel fork : node.forks) {

diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
index b48f722..6ed5a35 100644
--- a/src/main/java/com/gitblit/wicket/pages/RootPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -185,6 +185,11 @@
 		// navigation links
 		List<NavLink> navLinks = new ArrayList<NavLink>();
 		if (!authenticateView || (authenticateView && isLoggedIn)) {
+			UserModel user = UserModel.ANONYMOUS;
+			if (isLoggedIn) {
+				user = GitBlitWebSession.get().getUser();
+			}
+
 			navLinks.add(new PageNavLink(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
 					getRootPageParameters()));
 			if (isLoggedIn && app().tickets().isReady()) {
@@ -192,7 +197,9 @@
 			}
 			navLinks.add(new PageNavLink("gb.repositories", RepositoriesPage.class,
 					getRootPageParameters()));
-			navLinks.add(new PageNavLink("gb.filestore", FilestorePage.class, getRootPageParameters()));
+			if (user.canAdmin()) {
+				navLinks.add(new PageNavLink("gb.filestore", FilestorePage.class, getRootPageParameters()));
+			}
 			navLinks.add(new PageNavLink("gb.activity", ActivityPage.class, getRootPageParameters()));
 			if (allowLucene) {
 				navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class));
@@ -202,11 +209,6 @@
 				addDropDownMenus(navLinks);
 			}
 
-			UserModel user = UserModel.ANONYMOUS;
-			if (isLoggedIn) {
-				user = GitBlitWebSession.get().getUser();
-			}
-
 			// add nav link extensions
 			List<NavLinkExtension> extensions = app().plugins().getExtensions(NavLinkExtension.class);
 			for (NavLinkExtension ext : extensions) {
@@ -568,7 +570,7 @@
 					char[] password = RootPage.this.password.getObject().toCharArray();
 
 					HttpServletRequest request = ((WebRequest)RequestCycle.get().getRequest()).getHttpServletRequest();
-					
+
 					UserModel user = app().authentication().authenticate(username, password, request.getRemoteAddr());
 					if (user == null) {
 						error(getString("gb.invalidUsernameOrPassword"));
diff --git a/src/site/roadmap.mkd b/src/site/roadmap.mkd
deleted file mode 100644
index ea64321..0000000
--- a/src/site/roadmap.mkd
+++ /dev/null
@@ -1,8 +0,0 @@
-## Roadmap

-

-This is not exactly a formal roadmap but it is a priority list of what might be implemented in future releases.  

-This list is volatile and may not reflect what will be in the next release.

-

-* Add support for Project owners/administrators (ticket-75)

-* Add Project create/update pages

-* Integrate improvements for git-flow (ticket-55)

diff --git a/src/site/siteindex.mkd b/src/site/siteindex.mkd
index ae954bf..aec5c42 100644
--- a/src/site/siteindex.mkd
+++ b/src/site/siteindex.mkd
@@ -23,12 +23,9 @@
 		<tbody>

 		<tr><th>License</th><td><a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a></td></tr>

 		<tr><th>Sources</th><td><a href="${project.scmUrl}">GitHub</a></td></tr>		

-		<tr><th>Issues</th><td><a href="${project.issuesUrl}">GoogleCode</a></td></tr>

+		<tr><th>Issues</th><td><a href="${project.issuesUrl}">GitHub</a></td></tr>

 		<tr><th>Discussion</th><td><a href="${project.forumUrl}">Gitblit Group</a></td></tr>

-		<tr><th>Google+</th><td><a href="${project.socialNetworkUrl}">Gitblit+</a></td></tr>

 		<tr><th>Ohloh</th><td><a target="_top" href="http://www.ohloh.net/p/gitblit"><img border="0" width="100" height="16" src="http://www.ohloh.net/p/gitblit/widgets/project_thin_badge.gif" alt="Ohloh project report for Gitblit" /></a></td></tr>

-		<tr><th>Donations</th><td>If you enjoy Gitblit and want to support its development, please consider making a donation to <a href="http://www.stjude.org">St. Jude Children's Research Hospital</a>.

-		<a href="http://www.stjude.org" alt="St. Jude Children's Research Hospital"><img style="padding-top:10px;" src="stjude_150x150.gif"/></a></td></tr>

 		</tbody>

 		</table>

 	</div>