I was recently asked how to initialize a new Git repository with JGit, i.e. achieve what git init /path/to/repo does.

While creating a repository with JGit isn’t particularly difficult, there are a few details that might be worth noting. And because there are few online resources on the topic, and some of them misleading, this article summarizes how to use the JGit API to initialize a new Git repository.

Local Repositories

To initialize a repository with JGit, the InitCommand can be used. The command factory Git has a static method init() to create such a command.

Git git = Git.init().setDirectory( directory ).call();

When the above code is executed, a ne wGit repository is created. The location given in setDirectory() becomes the work directory that will contain the checked out files. If the directory does not exist, it will create it along the way.

A sub-directory named .git is created at the top level of the working directory. It contains the repositories history, configuration settings, pointers to branches, the index (aka staging area) and so on.

The screenshot to the left shows the internal structure of the .git directory. The refs directory will hold information about branches and tags. The actual contents will be stored in the objects directory. In the logs directory, changes to branches are recorded. For example, a commit or a checkout will create a log entry that can later be viewed with the git reflog command. The Explore Git Internals with the JGit API post I wrote some time ago goes more into the details of how Git manages the content.

To ensure that the command actually succeeded, the StatusCommand can be used to query the status of the repository much like git status would do. Unfortunately JGit’s status implementation slightly differs from native Git in that it doesn’t complain if there is no repository. This makes it necessary to check for an existing HEAD ref which indicates that there actually is a repository.

assertNotNull( git.getRepository().getRef( Constants.HEAD ) ); assertTrue( git.status().call().isClean() );

isClean() returns true for the newly initialized repository as there are no changed or untracked files in the work directory.

The assertion uses the Git instance that was returned by the InitCommand s call() method. This class serves as a factory and can be used to create Git commands (e.g. add , commit , checkout ) to be executed on that repository.



Newly initialized repositories have a peculiarity in that no branch has yet been created. Though there is a HEAD (the pointer to the current branch) that references a branch, (named master by default) this very branch does not exist. Newly initialized repositories have a peculiarity in that no branch has yet been created. Though there is a HEAD (the pointer to the current branch) that references a branch, (named master by default) this very branch does not exist. Usually, nothing to be worried about as with the first commit, the missing branch will be created. However, some operations like git branch create a branch and will fail with the slightly misleading error message ‘Ref HEAD cannot be resolved’ before an initial commit was made. This is also the case with native Git and not specific to JGit. To determine if a repository contains a commit yet, examine the HEAD ref like so: Ref headRef = git.getRepository().getRef( Constants.HEAD ); if( headRef == null || headRef.getObjectId() == null ) { // no commit yet }

Turn a Directory into a Repository

As seen before, the InitCommand creates missing directories if necessary. But the command can also be used on an existing directory, turning it thereby into a git repository.

The snippet below initializes a repository in an existing directory that contains a file, leaving its content intact.

F‌ile f‌ile = new F‌ile( "/path/to/existing/directory/readme.txt" ); f‌ile.createNewF‌ile(); Git git = Git.init().setDirectory( f‌ile.getParentF‌ile() ).call(); assertTrue( git.status().call().getUntracked().contains( f‌ile.getName() ) );

The status command reports the file as untracked. After adding the file to the index, it can be committed to the just created repository.

If the designated directory already holds a Git repository, there is no need to worry. In this case, JGit will do nothing and return a git instance that points to the already existing repository.

Separating Work and .git Directory

While by default, a repository has a work directory and its .git directory is located directly underneath, this is not required. A repository can have no working directory at all (discussed later) or its work directory at an entirely different location than the .git directory.

The init command below creates such a repository

Git git = Git.init().setDirectory( workDir ).setGitDir( gitDir ).call();

The resulting repository will be located at gitDir (this is where the history, branches, tags, etc. are stored) but will have its work directory at workDir .

The work directory configuration can also be changed for an existing repository. Either by manually editing the configuration file .git/config or through the JGit API.

StoredConfig config = git.getRepository().getConfig(); config.setString( "core", null, "worktree", workDir.getCanonicalPath() ); config.save();

Needless to say that the work directory content needs to be moved manually from the old to the new location.

Bare Repositories

The just created repository is meant to work with locally, also called a non-bare repository. Another type of Git repositories are bare repositories.

These are intended to be used as central repositories that will be shared by other users. No direct commits can be made to bare repositories. A bare repository receives commits in that they are pushed from a user’s local repository. Team members fetch commits made by others from this repository.

The snippet below will create such a bare repository.

Git git = Git.init().setDirectory( directory ).setBare( true ).call();

A bare repository does not have a working directory. Instead, the directory structure that can be found below the .git directory in a non-bare repository will be created directly in the given directory.

Since the status command requires a work directory, it cannot be used to verify that the above code succeeded. Instead the Repository instance should be bare and point to the desired directory like verified below.

assertTrue( git.getRepository().isBare() ); assertEquals( directory, git.getRepository().getDirectory() );

Note that querying the Repository for its work directory (i.e. calling getWorkTree() ) will throw a NoWorkTreeException for bare repositories.

Alternative API: Repository.create()

An alternative way to initialize a repository is to use the create method of Repository .

Repository repository = new F‌ileRepositoryBuilder().setGitDir( gitDir ).build(); repository.create(); assertNotNull( git.getRepository().getRef( Constants.HEAD ) ); assertTrue( Git.wrap( repository ).status().call().isClean() );

With the aid of F‌ileRepositoryBuilder a Repository instance is created that represents the not yet existing repository. Calling create() materializes the repository. The result is the same as the InitCommand would yield.

In order to create bare repositories, there is a also an overloaded create method: create( boolean bare ) .

What’s Next

If you are wondering what to do next with the just created repository, you may want to read Getting Started with JGit. The tutorial explains the most commonly used Git commands and their respective JGit counterparts. It walks through the steps to create a repository, fetch contents from a remote, add and remove files to/from the history, inspect the history, and finally push back the changes to the originating repository.

Conclusion

I hope this article helps to clarify how to create a new repository with JGit. The code that is used here is a collection of learning tests and can be found here in full length:

https://gist.github.com/rherrmann/4bacb68b23be1f12c73d

It illustrates the valid and invalid uses of the InitCommand API and may serve as a starting point for further experiments with JGit.

In order to help with setting up the development environment, you may want to also read An Introduction to the JGit Sources. If you have difficulties or further questions, please leave a comment or ask the friendly and helpful JGit community for assistance.