How to Access a Git Repository with JGit

Home  >>  Eclipse  >>  How to Access a Git Repository with JGit

How to Access a Git Repository with JGit

On September 22, 2014, Posted by , In Eclipse, By ,,, , With 13 Comments

A Git repository is represented in JGit through the Repository class that can be viewed as a handle to a repository. With a Repository instance, you can create JGit commands (through the Git factory class), gain access to configuration settings, resolve refs, etc.

There are several ways to obtain a repository reference, and because I have seen people having trouble here and there, this article summarizes how to access a Git repository with JGit.

Of Repositories and Builders

The class Repository is abstract to allow for implementations with different storage backends. For example, there is an InMemoryRepository for tests and experiments. But of course, most commonly used is the FileRepository, that represents a repository in the local filesystem. Because the actual implementations are considered internal, each Repository implementation has a corresponding repository builder that should be used to create instances thereof.

Hence the FileRepositoryBuilder is the recommended way to create a FileRepository. For historical reasons, there is also a RepositoryBuilder that does the same but will be removed with the next major version update.

Once you have configured a repository builder to your needs, call its build() method to have a repository created.

FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder();
repositoryBuilder.setMustExist( true );
repositoryBuilder.setGitDir( ... );
Repository repository = repositoryBuilder.build();

For layout reasons, I have put the method calls on a line each, but the FileRepositoryBuilder also implements a fluent interface so that method calls can be chained.


All methods discussed here apply to local repositories only. Git is a distributed version control system and as such is not designed to manipulate a remote repository directly.
To manipulate a remote repository, you have to clone it first. Now you can make modifications to the local copy, like commit new or changed files, create branches or tags, etc. To synchronize your changes with the remote repository, you will first have to integrate (aka fetch) changes (if any) from the remote and then finally push your local changes.

Prefer setGitDir()

In JGit, a file-based repository is identified through a directory. However, two directories may appear suitable to identify a repository: The work directory wherein the currently checked out revision resides and the git directory which holds the object database and metadata (e.g. branches, tags, etc.).

While FileRepositoryBuilder has a setGitDir() as well as a setWorkTree() method, I recommend to always use setGitDir(), because

  • By default, the git directory is a direct subdirectory of the work directory, but this can be overridden through an environment variable or a configuration setting.
  • And then there are bare repositories that don’t have a work directory at all.

Is This a Repository?

The FileRepositoryBuilder’s build() method returns a Repository whether or not a repository exists. Even if the given directory does not exist, an instance is returned.

I found two ways to test if a given directory actually points to an existing repository. By calling setMustExist(true), the FileRepositoryBuilder can be configured only to build existing repositories.
Once the must-exist flag is turned on, build() will throw a RepositoryNotFoundException if no repository could be found. As a side note, this behavior is undocumented. It might be just a lapse in the JavaDoc, and I doubt that this behavior will change, but still there is no guarantee as if it was part of the API.

Alternatively, you can test if the returned repository’s object database actually exists.

Repository repository = repositoryBuilder.build();
if( repository.getObjectDatabase().exists() ) {
  ...
}

As the name implies, ObjectDatabase.exists() returns true if there is an object database and false otherwise.

[EDIT 2014-11-12]
Unfortunately, the ObjectDatabase.exists() test has a flaw. Because Git creates the object database lazily, an empty, just initialized repository does not have an object database. Only after the first commit was done, the object database exists. Therefore, the test will return false for an empty repository even though it is a perfectly valid repository. What has proven a better approach so far is to test if the HEAD reference exists:

Repository repository = repositoryBuilder.build();
if( repository.getRef( "HEAD" ) != null ) {
  ...
}

Even an empty repository has a HEAD and getRef() returns only null if there is actually no repository.
[END EDIT]

A Small Detour: findGitDir()

The repository builder also offers a findGitDir() method that can be used to search for a repository starting from a given directory and going its parent hierarchy upwards.

FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder();
repositoryBuilder.addCeilingDirectory( new F‌ile( "/home/user" ) );
repositoryBuilder.findGitDir( new F‌ile( "/home/user/git/foo/bar" ) );

The search ends if either a repository directory was found or the root of the file system was reached.
getGitDir() can be used to obtain the search result and returns the found git directory or null if none was found.

The search can be limited by adding one or more ceiling directories. If one of them is reached while walking up the directory hierarchy, the search ends.

Alternative: Git.open()

If you find the FileRepositoryBuilder inconvenient to use, there is also a shorthand: Git.open().

Git git = Git.open( new F‌ile( "/path/to/repo/.git" ) );

The method expects a File parameter that denotes the directory in which the repository is located. The directory can either point to the work directory or the git directory. Again, I recommend to use the git directory here.

If the given directory does not exist or does not contain a repository, a RepositoryNotFoundException will be thrown. On success, an instance of class Git is returned that can be used to access the repository (git.getRepository()) and create git commands.

Another Detour: Submodules

Submodules allow putting a clone of another repository as a subdirectory within the socalled parent repository. Because a submodule is a repository in its own, it is also represented through a Repository instance in JGit.

Obtaining a reference to a submodule works just fine with the API outlined above:

F‌ile moduleDir = new F‌ile( parentRepo.getWorkTree(), "modules/foo/.git" );
repositoryBuilder.setGitDir( moduleDir );
Repository submoduleRepo = repositoryBuilder.build();

The git directory of the submodule is composed of the work directory of the parent repository plus the relative path to the the submodule’s git directory.

But the class SubmoduleWalk also offers static convenience methods:

Repository submoduleRepo = SubmoduleWalk.getSubmoduleRepository( parentRepo, "modules/foo" );
// or 
Repository submoduleRepo = SubmoduleWalk.getSubmoduleRepository( new F‌ile( "/path/to/parent/repo" ), "modules/foo" );

The first parameter denotes the parent repository – either through the given Repository or through its work directory location – and the second parameter specifies the name of the submodule.

More than One Way to Access a Git Repository with JGit

While Git.open() is nice and short, the FileRepositoryBuilder gives you more control and a reliable way to determine if the repository exists. Whether you prefer the first or the latter probably depends on your use case. Just remember not to use the FileRepository constructor directly as is may vanish or changes its behavior without prior notice.

If you like to try out the examples listed here for yourself, I recommend to setup JGit with access to its sources and JavaDoc so that you have meaningful context information, content assist, debug-sources, etc.


If you are wondering what to do next with a 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.

I hope this article helped to clarify how to access a repository with JGit. If you have further questions, please let me know in the comments

Follow me

Rüdiger Herrmann

Routined programmer, team lead, presenter, blog writer, and convinced open source contributor with two decades experience in the field.

Strong focus on quality backed up by agile methods like test driven development, modularization, pair programming, clean code, continuous integration.

Specialized in Java with JEE, REST, OSGi, RCP, RAP and building developer tools based on the Eclipse IDE (or any other platform if an opportunity arises).
Follow me

Latest posts by Rüdiger Herrmann (see all)

13 Comments so far:

  1. Thanks for the POST. I was stuck somewhere and this post helped me get my work done.

  2. tody says:

    I need to checkout to worktree directory, in Git it’s fairly easy:
    #!/bin/sh
    GIT_WORK_TREE=/var/www/example git checkout -f
    how to accomplish this in JGit
    or simply
    what is ‘git checkout -f ‘ and set GIT_WORK_TREE equivalents in JGit
    Thanks

    • Rüdiger Herrmann says:

      I am not sure if JGit understands the GIT_WORK_TREE environment variable, but if you need to explicitly set the work tree (which is usually not ncecesary), you can do so in JGit as described here: http://www.codeaffine.com/2015/05/06/jgit-initialize-repository/

      To checkout a work tree, you can use the CheckoutCommand like this:

      Git git = Git.open( "/path/to/repo" );
      git.checkout().setForce( true ).call();
      // don't forget do call git.close() when done

  3. renga says:

    I have one local git repository.In that,one particular folder contains more than one file with different commits.i want to get a specific version of a file.i have tried with git.checkout.setname(CommitId). The file is successfully checked out with the specific version.But the problem is ,other than the checked out file all other files in that folders are getting deleted.

  4. Manar says:

    Thanks for your interesting post ..

    I wonder how could I use JGit to retrieve information about old releases of certain open source project e.g. phpmyadmin .

    Particularly having a commit hash id , how can I get its auther, commit messege , diff , and the files changed in that commit ..

    I’m fairly new in this .. and your response will help a LOT

    Best

  5. Ashish Anand says:

    Hi,
    I am trying to connect GIT through my Java code to read a particular file. Can it be done by jgit?

    Please note the repository is placed at Atlassian bit bucket server.

    • Rüdiger Herrmann says:

      like with every Git repository, you will need to create a local clone of the remote repository first. However, that should be no problem with Bitbucket either. The JGit CloneCommand can be used like this:


      Git git = Git.cloneRepository()
      .setURI( "ssh://git@bitbucket.example.org:7999/..." )
      .setDirectory( "/path/to/local/repo" )
      .call();

      See the How to Clone Git Repositories with JGit article for more details.

  6. George Panam says:

    Thanks for this great post. Just to confirm, is cloning GIT repo necessary to push a file? I heard there is some plumbing api approach, does that help to push without cloning?

    • Rüdiger Herrmann says:

      Pushing without cloning is not possible conceptually. Think of pushing as copying files. With pushing, the commits you made on the cloned repository, the source, they will be published to the remote repository, the destination. Thus, without a clone, the ‘source’ part of a push operation is missing.

      To get a better understanding about the concepts of Git, you may want to read one of the many freely available books, like Pro Git, for example: https://git-scm.com/book/en/v2

Leave a Reply

Your email address will not be published. Required fields are marked *