javagitjgit

Check out single subdirectory from minimal repository using JGit


I'm using JGit 6.5.x with Java 17. I have a remote repository that is huge (gigabytes), but I only need temporary access to a single subdirectory (e.g. foo/bar/) for processing. The single subdirectory is really small (hundreds of kilobytes). Cloning a shallow, bare repository is relatively small as well:

try (final Git git = Git.cloneRepository()
    .setURI(REMOTE_REPOSITORY_URI.toASCIIString())
    .setDirectory(LOCAL_RESPOSITORY_PATH.toFile())
    .setBare(true)
    .setDepth(1)
    .call()) {
  System.out.println("cloned shallow, bare repository");
}

Is there a way to clone a shallow, bare repository like that (or any other minimal version of the repository), and then check out just the single subdirectory foo/bar to some other directory temporarily so that I can process those files using the normal Java file system API?

Note that I just now succeeded in the the clone above and haven't started looking into how I might check out just a single subdirectory from this bare repository.


Solution

  • Inspired by another answer I was able get a single-depth clone and check out only a single path without needing to do a bare clone, while using similar minimal file system space. The benefit to this approach is that only a single top-level directory is needed; the bare repository approach on the other hand requires a manual traversal and saving to a separate drop-level directory.

    The key is to use setNoCheckout(true) (in addition to setDepth(1)), and then after cloning manually perform a separate checkout specifying the requested path. Note that you must specify setStartPoint("HEAD") or specify a hash starting point, as there will be no branch because there is not yet a checkout.

    try (final Git git = Git.cloneRepository()
        .setURI(REMOTE_REPOSITORY_URI.toASCIIString())
        .setDirectory(LOCAL_RESPOSITORY_PATH.toFile())
        .setNoCheckout(true)
        .setDepth(1)
        .call()) {
    
      gitRepository.checkout()
        .setStartPoint("HEAD")
        .addPath("foo/bar")
        .call();
    
    }
    

    This seems to work very nicely! I would imagine it uses something similar to Satyajit Bhatt's answer under the hood.