Search code examples
multithreadinggitjgit

JGit: Is there a thread safe way to add and update files


The easy way to add or update files in JGit is like this:

  git.add().addFilepattern(file).call()

But that assumes that the file exists in the Git working directory. If I have a multi-threaded setup (using Scala and Akka), is there a way to work only on a bare repository, writing the data directly to JGit, avoiding having to first write the file in the working directory?

For getting the file, that seems to work with:

  git.getRepository().open(objId).getBytes()

Is there something similar for adding or updating files?


Solution

  • "Add" is a high-level abstraction that places a file in the index. In a bare repository, you lack an index, so this is not a 1:1 correspondence between the functionality. Instead, you can create a file in a new commit. To do this, you would use an ObjectInserter to add objects to the repository (one per thread, please). Then you would:

    1. Add the contents of the file to the repository, as a blob, by inserting its bytes (or providing an InputStream).

    2. Create a tree that includes the new file, by using a TreeFormatter.

    3. Create a commit that points to the tree, by using a CommitBuilder.

    For example, to create a new commit (with no parents) that contains only your file:

    ObjectInserter repoInserter = repository.newObjectInserter();
    ObjectId blobId;
    
    try
    {
        // Add a blob to the repository
        ObjectId blobId = repoInserter.insert(OBJ_BLOB, "Hello World!\n".getBytes());
    
        // Create a tree that contains the blob as file "hello.txt"
        TreeFormatter treeFormatter = new TreeFormatter();
        treeFormatter.append("hello.txt", FileMode.TYPE_FILE, blobId);
        ObjectId treeId = treeFormatter.insertTo(repoInserter);
    
        // Create a commit that contains this tree
        CommitBuilder commit = new CommitBuilder();
        PersonIdent ident = new PersonIdent("Me", "[email protected]");
        commit.setCommitter(ident);
        commit.setAuthor(ident);
        commit.setMessage("This is a new commit!");
        commit.setTreeId(treeId);
    
        ObjectId commitId = repositoryInserter.insert(commit);
    
        repoInserter.flush();
    }
    finally
    {
        repoInserter.release();
    }
    

    Now you can git checkout the commit id returned as commitId.