- Repository Owners may edit repositories through the web UI\r
- Automatically generates a self-signed certificate for https communications\r
- Git-notes support\r
+- Branch-selectable metrics\r
- Dates can optionally be displayed using the browser's reported timezone\r
- Author and Committer email address display can be controlled\r
- Dynamic zip downloads feature\r
- Unit testing\r
- Branch selector on Metrics\r
- Blame\r
+- Clone remote repository\r
\r
### Idea List\r
- Ticgit activity/timeline\r
- *server.httpBindInterface* and *server.httpsBindInterface*<br/>\r
**NOTE:** Consider using **https** exclusively because passwords for authentication are transmitted as clear text! \r
- *server.storePassword*<br/>\r
-**NOTE:** The certificate password AND the keystore password must match! \r
+**NOTE:** If you manually generate an ssl certificate, the certificate password AND the keystore password must match! \r
3. Execute `gitblit.cmd` or `java -jar gitblit.jar` from a command-line\r
4. Wait a minute or two while all dependencies are downloaded and your self-signed certificate is generated.\r
5. Open your browser to <http://localhost> or <https://localhost> depending on your chosen configuration.\r
Repository names must be unique and are CASE-SENSITIVE ON CASE-SENSITIVE FILESYSTEMS. The name must be composed of letters, digits, or `/ _ - .`<br/>\r
Whitespace is illegal.\r
\r
-Repositories can be grouped by folders. e.g. *libraries/mycoollib.git* and *libraries/myotherlib.git*\r
+Repositories can be grouped within subfolders. e.g. *libraries/mycoollib.git* and *libraries/myotherlib.git*\r
\r
-Repository names will automatically have *.git* appended to the name at creation time, if not already specified. \r
+All created repositories are *bare* and will automatically have *.git* appended to the name at creation time, if not already specified. \r
\r
#### Repository Owner\r
The *Repository Owner* has the special permission of being able to edit a repository through the web UI. The Repository Owner is not permitted to rename the repository, delete the repository, or reassign ownership to another user.\r
### Creating your own Self-Signed Certificate\r
\r
Review the contents of the `makekeystore.cmd` or `makekeystore_jdk.cmd` script and execute it.<br/>\r
-**NOTE:** The certificate password AND the keystore password must match!\r
+**NOTE:** If you manually generate an ssl certificate, the certificate password AND the keystore password must match!\r
\r
### Running as a Service\r
Review the contents of the `installService.cmd` or `installService64.cmd`, as appropriate for your installed Java Virtual Machine.<br/>\r
### Why use Gitblit?\r
It's a small tool that allows you to easily manage shared repositories and doesn't require alot of setup or git kung-foo.\r
\r
+### Who is the target user for Gitblit?\r
+Small workgroups that require centralized repositories.\r
+\r
+Gitblit is not meant to be a social coding resource like [Github](http://github.com) or [Bitbucket](http://bitbucket.com) with 100s or 1000s of users. Gitblit is designed to fulfill the same function as your centralized Subversion or CVS server.\r
+\r
+### Why does Gitblit exist?\r
+As a Java developer I prefer that as much of my tooling as possible is Java.<br/>\r
+Originally, I was going to use [Mercurial](http://mercurial.selenic.com) but...\r
+\r
+- MercurialEclipse [shells to Python and captures System.in](http://mercurial.808500.n3.nabble.com/Hg4J-Mercurial-pure-Java-library-tp2693090p2694555.html)<br/>\r
+Parsing command-line output is fragile and suboptimal.<br/>Unfortunately this is necessary because Mercurial is an application, not a library.\r
+- Mercurial seems to [frown](http://mercurial.808500.n3.nabble.com/Hg4J-Mercurial-pure-Java-library-tp2693090p2695051.html) on the fledgling [Hg4j][hg4j] (pure Java Mercurial) project.\r
+- Mercurial HTTP/HTTPS needs to run as CGI through Apache/IIS/etc, as mod_python through Apache, or served with a built-in http server.<br/>\r
+This requires setup and maintenance of multiple, mixed 3rd party components.\r
+\r
+Gitblit eliminates all that complication with its 100% Java stack and simple single configuration file.\r
+\r
### Do I need real Git?\r
No. Gitblit is based on [JGit][jgit] which is a pure Java implementation of the [Git version control system][git].<br/>\r
Everything you need for Gitblit is either in the zip distribution file or automatically downloaded on execution.\r
\r
+### Can I run Gitblit in conjunction with my existing Git tooling?\r
+Yes. You can configure Gitblit to only be a repository viewer.\r
+\r
### Do I need a JDK or can I use a JRE?\r
Gitblit will run just fine with a JRE. Gitblit can optionally use `keytool` from the JDK to generate self-signed certificates, but normally Gitblit uses [BouncyCastle][bouncycastle] for that need.\r
\r
[jgit]: http://eclipse.org/jgit "Eclipse JGit Site"\r
[git]: http://git-scm.com "Official Git Site"\r
[mina]: http://mina.apache.org "Apache Mina"\r
-[bouncycastle]: http://bouncycastle.org "The Legion of the Bouncy Castle"
\ No newline at end of file
+[bouncycastle]: http://bouncycastle.org "The Legion of the Bouncy Castle"\r
+[hg4j]: http://code.google.com/p/hg4j/ "hg4j"
\ No newline at end of file
Repository r = null;\r
if (isCreate) {\r
// ensure created repository name ends with .git\r
- if (!repository.name.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT_EXT)) {\r
+ if (!repository.name.toLowerCase().endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT_EXT)) {\r
repository.name += org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;\r
}\r
if (new File(repositoriesFolder, repository.name).exists()) {\r
}\r
// create repository\r
logger.info("create repository " + repository.name);\r
- r = JGitUtils.createRepository(repositoriesFolder, repository.name, true);\r
+ r = JGitUtils.createRepository(repositoriesFolder, repository.name);\r
} else {\r
// rename repository\r
if (!repositoryName.equalsIgnoreCase(repository.name)) {\r
import java.io.OutputStream;\r
import java.nio.charset.Charset;\r
import java.util.ArrayList;\r
+import java.util.Arrays;\r
import java.util.Collection;\r
import java.util.Collections;\r
import java.util.Date;\r
import java.util.zip.ZipEntry;\r
import java.util.zip.ZipOutputStream;\r
\r
+import org.eclipse.jgit.api.CloneCommand;\r
+import org.eclipse.jgit.api.FetchCommand;\r
import org.eclipse.jgit.api.Git;\r
import org.eclipse.jgit.diff.DiffEntry;\r
import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
import org.eclipse.jgit.revwalk.RevTree;\r
import org.eclipse.jgit.revwalk.RevWalk;\r
import org.eclipse.jgit.revwalk.filter.RevFilter;\r
+import org.eclipse.jgit.storage.file.FileRepository;\r
+import org.eclipse.jgit.transport.FetchResult;\r
+import org.eclipse.jgit.transport.RefSpec;\r
import org.eclipse.jgit.treewalk.TreeWalk;\r
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;\r
import org.eclipse.jgit.treewalk.filter.OrTreeFilter;\r
return r.toString().trim();\r
}\r
\r
- public static Repository createRepository(File repositoriesFolder, String name, boolean bare) {\r
- Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(bare).call();\r
+ public static FetchResult cloneRepository(File repositoriesFolder, String name, String fromUrl) throws Exception {\r
+ FetchResult result = null;\r
+ if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {\r
+ name += Constants.DOT_GIT_EXT;\r
+ }\r
+ File folder = new File(repositoriesFolder, name);\r
+ if (folder.exists()) {\r
+ File gitDir = FileKey.resolve(new File(repositoriesFolder, name), FS.DETECTED);\r
+ FileRepository repository = new FileRepository(gitDir);\r
+ result = fetchRepository(repository);\r
+ repository.close();\r
+ } else {\r
+ CloneCommand clone = new CloneCommand();\r
+ clone.setBare(true);\r
+ clone.setCloneAllBranches(true);\r
+ clone.setURI(fromUrl);\r
+ clone.setDirectory(folder);\r
+ clone.call();\r
+ // Now we have to fetch because CloneCommand doesn't fetch\r
+ // refs/notes nor does it allow manual RefSpec.\r
+ File gitDir = FileKey.resolve(new File(repositoriesFolder, name), FS.DETECTED);\r
+ FileRepository repository = new FileRepository(gitDir);\r
+ result = fetchRepository(repository);\r
+ repository.close();\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public static FetchResult fetchRepository(Repository repository, RefSpec... refSpecs)\r
+ throws Exception {\r
+ Git git = new Git(repository);\r
+ FetchCommand fetch = git.fetch();\r
+ List<RefSpec> specs = new ArrayList<RefSpec>();\r
+ if (refSpecs == null || refSpecs.length == 0) {\r
+ specs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));\r
+ specs.add(new RefSpec("+refs/tags/*:refs/tags/*"));\r
+ specs.add(new RefSpec("+refs/notes/*:refs/notes/*"));\r
+ } else {\r
+ specs.addAll(Arrays.asList(refSpecs));\r
+ }\r
+ fetch.setRefSpecs(specs);\r
+ FetchResult result = fetch.call();\r
+ repository.close();\r
+ return result;\r
+ }\r
+\r
+ public static Repository createRepository(File repositoriesFolder, String name) {\r
+ Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(true).call();\r
return git.getRepository();\r
}\r
\r
refs.put(objectid, new ArrayList<RefModel>());\r
}\r
refs.get(objectid).add(ref);\r
- } \r
+ }\r
return refs;\r
}\r
\r
package com.gitblit.tests;\r
\r
import java.io.File;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
\r
import junit.extensions.TestSetup;\r
import junit.framework.Test;\r
import junit.framework.TestSuite;\r
\r
-import org.eclipse.jgit.api.CloneCommand;\r
-import org.eclipse.jgit.api.FetchCommand;\r
-import org.eclipse.jgit.api.Git;\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.storage.file.FileRepository;\r
-import org.eclipse.jgit.transport.RefSpec;\r
\r
import com.gitblit.FileSettings;\r
import com.gitblit.GitBlit;\r
import com.gitblit.GitBlitException;\r
import com.gitblit.JettyLoginService;\r
import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.utils.JGitUtils;\r
\r
public class GitBlitSuite extends TestSetup {\r
public static final File REPOSITORIES = new File("git");\r
}\r
\r
public static Repository getJGitRepository() throws Exception {\r
- return new FileRepository(new File(REPOSITORIES, "nested/jgit.git"));\r
+ return new FileRepository(new File(REPOSITORIES, "test/jgit.git"));\r
}\r
\r
public static Repository getBluezGnomeRepository() throws Exception {\r
- return new FileRepository(new File(REPOSITORIES, "nested/bluez-gnome.git"));\r
+ return new FileRepository(new File(REPOSITORIES, "test/bluez-gnome.git"));\r
}\r
\r
@Override\r
GitBlit.self().setLoginService(loginService);\r
\r
if (REPOSITORIES.exists() || REPOSITORIES.mkdirs()) {\r
- cloneOrFetch("helloworld.git", "https://github.com/git/hello-world.git", true);\r
- cloneOrFetch("ticgit.git", "https://github.com/jeffWelling/ticgit.git", true);\r
- cloneOrFetch("nested/bluez-gnome.git", "https://git.kernel.org/pub/scm/bluetooth/bluez-gnome.git", true);\r
- cloneOrFetch("nested/jgit.git", "https://github.com/eclipse/jgit.git", true);\r
- cloneOrFetch("nested/helloworld.git", "https://github.com/git/hello-world.git", true);\r
+ cloneOrFetch("helloworld.git", "https://github.com/git/hello-world.git");\r
+ cloneOrFetch("ticgit.git", "https://github.com/jeffWelling/ticgit.git");\r
+ cloneOrFetch("test/bluez-gnome.git",\r
+ "https://git.kernel.org/pub/scm/bluetooth/bluez-gnome.git");\r
+ cloneOrFetch("test/jgit.git", "https://github.com/eclipse/jgit.git");\r
+ cloneOrFetch("test/helloworld.git", "https://github.com/git/hello-world.git");\r
\r
enableTickets("ticgit.git");\r
enableDocs("ticgit.git");\r
showRemoteBranches("ticgit.git");\r
- showRemoteBranches("nested/jgit.git");\r
+ showRemoteBranches("test/jgit.git");\r
}\r
}\r
\r
- private void cloneOrFetch(String toFolder, String fromUrl, boolean bare) throws Exception {\r
- File folder = new File(REPOSITORIES, toFolder + (bare ? "" : "/.git"));\r
- if (folder.exists()) {\r
- System.out.print("Updating " + (bare ? "bare " : " ") + toFolder + "... ");\r
- fetch(toFolder);\r
- System.out.println("done.");\r
- } else {\r
- System.out.println("Cloning " + (bare ? "bare " : " ") + toFolder + "... ");\r
- CloneCommand clone = new CloneCommand();\r
- clone.setBare(bare);\r
- clone.setCloneAllBranches(true); \r
- clone.setURI(fromUrl);\r
- clone.setDirectory(folder);\r
- clone.call();\r
- // Now we have to fetch because CloneCommand doesn't fetch\r
- // Notes nor does it allow manual RefSpec.\r
- fetch(toFolder);\r
- System.out.println("done.");\r
- }\r
- }\r
- \r
- private void fetch(String toFolder) throws Exception {\r
- FileRepository repository = new FileRepository(new File(REPOSITORIES, toFolder));\r
- Git git = new Git(repository);\r
- FetchCommand fetch = git.fetch();\r
- List<RefSpec> specs = new ArrayList<RefSpec>();\r
- specs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));\r
- specs.add(new RefSpec("+refs/tags/*:refs/tags/*"));\r
- specs.add(new RefSpec("+refs/notes/*:refs/notes/*"));\r
- fetch.setRefSpecs(specs);\r
- fetch.call();\r
- repository.close();\r
+ private void cloneOrFetch(String name, String fromUrl) throws Exception {\r
+ System.out.print("Fetching " + name + "... ");\r
+ JGitUtils.cloneRepository(REPOSITORIES, name, fromUrl);\r
+ System.out.println("done.");\r
}\r
\r
private void enableTickets(String repositoryName) {\r
g.printStackTrace();\r
}\r
}\r
- \r
+\r
private void enableDocs(String repositoryName) {\r
try {\r
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);\r
g.printStackTrace();\r
}\r
}\r
- \r
+\r
private void showRemoteBranches(String repositoryName) {\r
try {\r
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);\r
import org.eclipse.jgit.lib.ObjectId;\r
import org.eclipse.jgit.lib.PersonIdent;\r
import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.util.FS;\r
\r
import com.gitblit.GitBlit;\r
import com.gitblit.Keys;\r
\r
public void testCreateRepository() throws Exception {\r
String[] repositories = { "NewTestRepository.git", "NewTestRepository" };\r
- for (String repositoryName : repositories) {\r
- boolean isBare = repositoryName.endsWith(".git");\r
+ for (String repositoryName : repositories) { \r
Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES,\r
- repositoryName, isBare);\r
- File folder;\r
- if (isBare) {\r
- folder = new File(GitBlitSuite.REPOSITORIES, repositoryName);\r
- } else {\r
- folder = new File(GitBlitSuite.REPOSITORIES, repositoryName + "/.git");\r
- }\r
+ repositoryName);\r
+ File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED);\r
assertTrue(repository != null);\r
assertFalse(JGitUtils.hasCommits(repository));\r
assertTrue(JGitUtils.getFirstCommit(repository, null) == null);\r
}\r
\r
public void testBranches() throws Exception {\r
- Repository repository = GitBlitSuite.getTicgitRepository();\r
+ Repository repository = GitBlitSuite.getJGitRepository();\r
for (RefModel model : JGitUtils.getLocalBranches(repository, true, -1)) {\r
assertTrue(model.getName().startsWith(Constants.R_HEADS));\r
assertTrue(model.equals(model));\r
+ model.getName().hashCode());\r
assertTrue(model.getShortMessage().equals(model.getShortMessage()));\r
}\r
- assertTrue(JGitUtils.getRemoteBranches(repository, true, 10).size() == 10);\r
+ assertTrue(JGitUtils.getRemoteBranches(repository, true, 8).size() == 8);\r
repository.close();\r
}\r
\r
public void testTags() throws Exception {\r
- Repository repository = GitBlitSuite.getTicgitRepository();\r
+ Repository repository = GitBlitSuite.getJGitRepository();\r
for (RefModel model : JGitUtils.getTags(repository, true, -1)) {\r
- if (model.getObjectId().getName().equals("283035e4848054ff1803cb0e690270787dc92399")) {\r
+ if (model.getObjectId().getName().equals("d28091fb2977077471138fe97da1440e0e8ae0da")) {\r
assertTrue("Not an annotated tag!", model.isAnnotatedTag());\r
}\r
assertTrue(model.getName().startsWith(Constants.R_TAGS));\r