在 Java 中,我如何创建一个线程安全的应用程序来重用 Git 存储库?

In Java, how can I make a thread-safe application which reuses Git repositories?

我有一个开源 Web 应用程序,其中磁盘上的同一个存储库可以由多个线程使用。这涉及对新分支执行 git checkout 并从存储库中访问文件。

这有一些问题:

我考虑过使用同步方法和信号量作为解决方案,但我不知道 "best" 在这种情况下的解决方案。

这里有一些选项,它们不会破坏磁盘上的文件,因此对多线程处理更安全:

  • 您可以直接查看文件的内容而无需签出整个提交:
    在命令行中,您可以使用:

    git show <tree-ish>:path/to/file
    

    或者以某种方式找到文件内容的哈希值,然后调用:

    git cat-file -p <file-hash>
    

    我不熟悉 JGit,但是您肯定可以找到一种方法来使用它的 api

  • 执行这些命令中的任何一个
  • 如果你真的有理由签出一个完整的提交,你可以签出到不同的工作树(参见 git help worktree),
    或者可能构建一个存档(git help archive)而不是真正检查提交

奖励点:所有这些命令也适用于裸 git 存储库。

我建议使用 JGit,Git 的纯 Java 实现。使用普通的 Java 库不需要在服务器上提供合适的 Git 版本,并且还可以节省一些处理周期,因为它不会为每个 Git 命令生成一个单独的进程。

在大多数领域,JGit 与 Git CLI 实现不相上下。因此,除非您需要非常具体的 Git 功能,否则您不会看到任何差异。

为了直接访问 blob 的内容,可以使用 ObjectReader/ObjectLoader API。例如:

ObjectReader objectReader = repository.newObjectReader();
ObjectLoader objectLoader = objectReader.open( blobId );
int type = objectLoader.getType(); // Constants.OBJ_BLOB
byte[] contents = objectLoader.getBytes();

有关直接访问 Git 对象数据库的更多信息,请参阅这篇文章:http://www.codeaffine.com/2014/10/20/git-internals/

为了防止并发写入访问,JGit 使用与 Git CLI 相同的锁定文件。如果写访问由于锁定失败而失败,JGit returns 相应的命令状态,您允许应用程序代码稍后重试相同的操作。

如果 'optimistic locking' 的方法不适合您的用例,您仍然可以求助于工作队列或其他同步方式。

尝试scm4j-vcs-api。它有一个特殊功能 - 锁定工作副本,它是一个线程和进程安全的文件夹

public static final String WORKSPACE_DIR = System.getProperty("java.io.tmpdir") + "scm4j-vcs-workspaces";
public static void main(String[] args) {
    IVCSWorkspace workspace = new VCSWorkspace(WORKSPACE_DIR);
    String repoUrl = "https://github.com/scm4j/scm4j-vcs-api";
    IVCSRepositoryWorkspace repoWorkspace = workspace.getVCSRepositoryWorkspace(repoUrl);
    try (IVCSLockedWorkingCopy wc = repoWorkspace.getVCSLockedWorkingCopy()) {
        // execute git-related operations within wc.getFolder()
    }
}

另请参阅 scm4j-vcs-git 作为示例库,它在单独的工作副本中执行 Git 操作