Software Development

How to Clone Git Repositories with JGit

Whatever you plan to do with an existing repository, first a clone has to be created. Whether you plan to contribute or just want to peek at its history, a local copy of the repository is needed.

While cloning 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 subject, this article summarizes how to use the JGit API to clone from an existing Git repository.

Cloning Basics

To make a local copy of a remote repository, the CloneCommand needs at least to be told where the remote is to be found:

Git git = Git.cloneRepository()
  .setURI( "" )

The Git factory class has a static cloneRepository() method that returns a new instance of a CloneCommand. setURI() advises it where to clone from and like with all JGit commands, the call() method actually executes the command.

Though remote repositories – like the name suggests – are usually stored on a remote host, the location given in setURI() can also be a path to a local resource.

If no more information is given, JGit will choose the directory in which the cloned repository will be stored for you. Based on the current directory and the repository name that is derived from its URL, a directory name is built. In the example above it would be ‘/path/to/current/jgit’.

But usually you would want to have more control over the destination directory and explicitly state where to store the local clone.

The setDirectory() method specifies where the work directory should be and with setGitDir() the location of the metadata directory (.git) can be set. If setGitDir() is omitted, the .git directory is created directly underneath the work directory

The example below

Git git = Git.cloneRepository()
  .setURI( "" )
  .setDirectory( "/path/to/repo" )

will create a local repository whose work directory is located at ‘/path/to/repo’ and whose metadata directory is located at ‘/path/to/repo/.git’.

However the destination location is chosen, explicitly through your code or by JGit, the designated directory must either be empty or must not exist. Otherwise an exception will be thrown.

The settings for setDirectory(), setGitDir() and setBare() (see below) are forwarded to the InitCommand that is used internally by the CloneCommand. Hence more details thereover are explained in Initializing Git Repositories with JGit.

The Git instance that is returned by provides access to the repository itself (git.getRepository()) and can be used to execute further commands targeting this repository. When finished using the repository it must be closed (git.close()) or otherwise the application may leak file handles.

To later regain a Repository (or Git) instance, the path to the work directory or .git directory is sufficient. The article How to Access a Git Repository with JGit has detailed information on the subject.

Upstream Configuration

As a last step the clone command updates the configuration file of the local repository to register the source repository as a socalled remote.

When looking at the configuration file (.git/config) the remote section looks like this:

[remote "origin"]
  url =
  fetch = +refs/heads/*:refs/remotes/origin/*

If no remote name is given, the defaults ‘origin’ is used. In order to have the CloneCommand use a certain name under which the remote repository is registered, use setRemote().

The refspec given by ‘fetch’ determines which branches should be exchanged when fetching from or pushing to the remote repository by default.

Cloning Branches

By default, the clone command creates a single local branch. It looks at the HEAD ref of the remote repository and creates a local branch with the same name as the remote branch referenced by it.

But the clone command can also be told to clone and checkout certain branch(es). Assuming that the remote repository has a branch named ‘extra’, the following lines will clone this branch.

Git git = Git.cloneRepository()
  .setURI( "" )
  .setDirectory( "/path/to/repo" )
  .setBranchesToClone( singleton( "refs/heads/extra" ) );
  .setBranch( "refs/heads/extra" )

With setBranchesToClone(), the command clones only the specified branches. Note that the setBranch() directive is necessary to also checkout the desired branch. Otherwise, JGit would attempt to checkout the ‘master’ branch. While this is isn’t a problem from a technical point of view, it is usually not what you want.

If all branches of the remote repository should be cloned, you can advise the command like so:

Git git = Git.cloneRepository()
  .setURI( "" )
  .setDirectory( "/path/to/repo" )
  .setCloneAllBranches( true )

To prevent the current branch from being checked out at all, the setNoCheckout() method can be used.

Listing Remote Branches

If you want to know which branches a remote repository has to offer, the LsRemoteCommand comes to the rescue. To list all branches of a JGit repository, use Git’s lsRemoteRepository() like shown below.

Collection<Ref> remoteRefs = Git.lsRemoteRepository()
  .setHeads( true )
  .setRemote( "" )

In case you would also want to list tags, advise the command with setTags( true ) to include tags.

For reasons I rather don’t want to know, JGit requires a local repository for certain protocols in order to be able to list remote refs. In this case Git.lsRemoteRepository() will throw a NotSupportedException. The workaround is to create a temporary local repository and use git.lsRemote() instead of Git.lsRemoteRepository() where git wraps the temporary repository.

Cloning Bare Repositories

If the local repository does not need a work directory, the clone command can be instructed to create a bare repository.

By default non-bare repositories are created, but with setBare( true ) a bare repository is created like shown below:

Git git = Git.cloneRepository()
  .setBare( true )
  .setURI( "" )
  .setGitDir( "/path/to/repo" )

Here the destination directory is specified via setGitDir() instead of using setDirectory().
The resulting repository’s isBare() will return true, getGitDir() will return /path/to/repo and since there is no work directory getWorkTree() will throw a NoWorkTreeException.

Note that ‘bare’ here only applies to the destination repository. Whether the source repository is bare or not doesn’t make a difference when cloning.

Cloning Submodules

If the remote repository is known to have submodules or if you wish to include submodules in case there are any, the clone command can be instructed to do so:

Git git = Git.cloneRepository()
  .setCloneSubmodules( true )
  .setURI( "" )
  .setDirectory( "/path/to/repo" )

The above example advises the clone command to also clone any submodule that is found.

If setCloneSubmodules( true ) wan’t specified while cloning the repository, you can catch up on the missing submodules later. For more details see the article How to manage Git Submodules with JGit.

Cloning with Authentication

Of course, JGit also allows to access repositories that require authentication. Common protocols like SSH and HTTP(S) and their authentication methods are supported. A detailed explanation on how to use authentication support can be found in the JGit Authentication Explained article.

Concluding How to Clone Git Repositories with JGit

For almost all features of the native Git clone command there is an equivalent in JGit. Even a progress monitor which may be useful when JGit is embedded in interactive applications exists. And for the missing mirror option apparently a workaround exists. Only the often asked for shallow clones (e.g. git clone --depth 2) aren’t yet supported by JGit.

The snippets shown throughout this article are excerpts from a learning test that illustrates the common use cases of the CloneCommand.

If you still have difficulties or questions, feel free to leave a comment or ask the friendly and helpful JGit community for assistance.

Reference: How to Clone Git Repositories with JGit from our JCG partner Rudiger Herrmann at the Code Affine blog.
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments
Back to top button