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 25 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.

See also  Getting Started with JGit

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.

See also  Terminate and Relaunch in Eclipse

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

Rüdiger Herrmann
Follow me
Latest posts by Rüdiger Herrmann (see all)

25 Comments so far:

  1. Rohit Vaidya says:

    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

      • renga says:

        how to get the particular commit of a specific file from a local repository

        • Rüdiger Herrmann says:

          I am afraid I don’t understand your question. Could you please clarify your problem and also post what you have tried so far?

  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.

      • Maria says:

        Is it possible to connect to GIT without cloning the repository? via https ?

        • Rüdiger Herrmann says:

          It depends which information you require after you ‘connect to GIT’.

          Though it is possible to connect to a Git repository with HTTPS, without cloning the repository, you can only retrieve which refs (i.e. branches) there are.

          For any other information, e.g. the history of commits, certain file revisions, etc., the repository needs to be cloned first. After that, the local copy of the repository can be accessed to reveal such information.

          Note, that this isn’t specific to JGit but holds true for Git repositories in general, regardless which means is used to access. For background information, please see other questions on this page or the respective post on SO and elsewhere.

  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

  7. Tim Cederquist says:

    Is it possible to pull/clone only one file from a large remote repository? I’d like to edit one file and push it back into the remote but don’t want to have the user wait for a large clone operation that takes quite a while for a simple one file edit that I am supplying through a web site interface. Example: pull/edit/push a small text file edit without pulling thousands of other files in the repo for instance.

  8. Tim Cederquist says:

    Wow that was a fast response! Thanks, saved me a lot of time searching for a solution that doesn’t exist. I guess next step is to explore using a full clone local repo as a cache/proxy and reduce the overhead by using branches for each user working on a file then. My needs are simplified as its a proxy for content hosted in a database that I’m bridging to git as the deploy tools are all git based and edit tools are all mysql based. Unless you have other ideas for a bridge driven from a java rest service? It’s an odd problem with tons of legacy and client requirements but a fun puzzle.

    • Rüdiger Herrmann says:

      Having a single ‘cache’ repository with branches for each user/edit-operation sounds reasonable. Be aware that you need to coordinate access to the work directory. If there is too much traffic to lock the work directory for every edit operation, you may want to explore the techniques described here to create commits without using a work directory.

  9. Ramachandra says:

    could you be please help me how to create a git hub repository through java code with some file and also how to create a branch in the recent created repository?

    Thanks
    Ram

  10. Tebogo says:

    HI
    when i retrieve a file using a specific commit ID, the file comes out with a bunch of commit id, tree ids etc on it. here is my code below, am i missing something?

    public void init() throws IOException, GitAPIException {

    Git git = Git.open(new File(“/C:/temp/testing/”));
    File file = new File(“C:/temp/testing/”,”helloworld.txt”);

    git.add().addFilepattern(file.getName()).call();
    RevCommit test_commit = git.commit().setMessage(“add text file just”).call();

    get(file.getName(), test_commit.getId().toObjectId().getName(), git.getRepository(),test_commit.getId());
    System.out.println(test_commit.toString());

    }

    public void get(String file, String commid, Repository repository, ObjectId id) {
    try {

    System.out.println(“Looking up file {} in revision {}” + file + commid);

    ObjectId obj = ObjectId.fromString(commid);
    byte[] filecontent = repository.open(obj).getCachedBytes();
    File files = getLocalFileForByteArray(filecontent,file);
    System.out.println(“done”);

    } catch (Exception e) {
    }
    }

    public static File getLocalFileForByteArray(final byte[] fileContents, final String fileName) throws IOException {
    final Path tempDirectory = Files.createTempDirectory(“import”);
    final Path localTempFile = Files.createFile(Paths.get(tempDirectory.toString(), fileName));
    final ByteArrayInputStream byteInputStream = new ByteArrayInputStream(fileContents);
    FileCopyUtils.copy(byteInputStream, Files.newOutputStream(localTempFile));
    return localTempFile.toFile();

    }

  11. Priyanhuarya says:

    Hello Rüdiger Herrmann,

    Thank you for great post. is there any way to fork the repo using Jgit API?
    I want to fork the repository under logged in user using ex -CredentialsProvider(new UsernamePasswordCredentialsProvider(“username”,”pwd”)