使用JGit从最小存储库中检出单个子目录

mfuanj7w  于 2023-06-04  发布在  Git
关注(0)|答案(2)|浏览(253)

我使用JGit 6.5.x和Java 17。我有一个巨大的远程存储库(千兆字节),但我只需要临时访问一个子目录(例如:foo/bar/)进行处理。单个子目录非常小(数百KB)。克隆一个浅的、空的存储库也相对较小:

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");
}

有没有一种方法可以克隆这样一个浅的、空的存储库(或任何其他最小版本的存储库),然后暂时将单个子目录foo/bar checkout 到其他目录,以便我可以使用普通的Java文件系统API处理这些文件?
请注意,我刚刚成功地完成了上面的克隆,还没有开始研究如何从这个裸存储库中检出单个子目录。

omjgkv6w

omjgkv6w1#

尝试以下解决方案:
注意:在应用任何git更改之前,请确保您有必要文件的备份。
使用git对象创建一个TreeWalk,它允许你遍历仓库的树并找到你感兴趣的子目录。将起始路径指定为存储库的根目录:

try (Git git = Git.open(LOCAL_REPOSITORY_PATH.toFile())) {
    Repository repository = git.getRepository();

    // Get the tree for the repository's HEAD commit
    RevWalk revWalk = new RevWalk(repository);
    RevCommit commit = revWalk.parseCommit(repository.resolve(Constants.HEAD));
    RevTree tree = commit.getTree();

    // Create a TreeWalk starting from the root of the repository
    TreeWalk treeWalk = new TreeWalk(repository);
    treeWalk.addTree(tree);
    treeWalk.setRecursive(true);
    
    // Specify the path of the subdirectory you want to check out
    treeWalk.setFilter(PathFilter.create("foo/bar"));

    if (!treeWalk.next()) {
        throw new IllegalStateException("Subdirectory not found");
    }

    // Get the ObjectId of the subdirectory's tree
    ObjectId subdirectoryTreeId = treeWalk.getObjectId(0);
    treeWalk.close();
    
    // Create a new Git object with the shallow, bare repository
    Git subGit = new Git(repository);

    // Checkout the subdirectory's tree to a temporary directory
    Path temporaryDirectory = Files.createTempDirectory("subdirectory");
    subGit.checkout().setStartPoint(subdirectoryTreeId.getName()).setAllPaths(true).setForce(true).setTargetPath(temporaryDirectory.toFile()).call();

    // Now you can use the Java file system API to process the files in the temporary directory
    
    // Clean up the temporary directory when you're done
    FileUtils.deleteDirectory(temporaryDirectory.toFile());
}

在上面的代码中,我们使用TreeWalk遍历存储库的树并找到您指定的子目录(foo/bar)。然后获取子目录树的ObjectId,并使用仓库创建一个新的Git对象。最后,我们使用checkout()将子目录的树检出到一个临时目录,您可以使用Java文件系统API来处理该目录中的文件。完成后不要忘记清理临时目录。
请注意,代码假设您已经有了必要的JGit和Java IO导入。

6jygbczu

6jygbczu2#

another answer的启发,我能够获得一个单深度克隆,并只检出一个路径,而不需要执行裸克隆,同时使用类似的最小文件系统空间。这种方法的好处是只需要一个顶级目录;另一方面,裸存储库方法需要手动遍历并保存到单独的下拉级目录。
关键是使用setNoCheckout(true)(除了setDepth(1)之外),然后在克隆后手动执行单独的 checkout ,指定所请求的路径。* 请注意,您必须指定setStartPoint("HEAD")或指定哈希起点,因为还没有 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();

}

这似乎工作得很好!我可以想象它在引擎盖下使用类似于Satyajit Bhatt's answer的东西。

相关问题