9

I need to clone an existing git repository into an InMemoryRepository, using JGit, change a file's content and push the changes back to the remote repository.

I couldn't find any examples of cloning a repository into an in-memory repository.

I tried this:

InMemoryRepository.Builder builder = new InMemoryRepository.Builder();  
InMemoryRepository inm = builder.build(); 
Git.cloneRepository().setURI("git@[github_url].git").setDirectory(inm.getDirectory()).call();  

Which resulted in an error:

'Destination path ".git" already exists and is not an empty directory'.

I checked the configuration options for InMemoryRepository.Builder and Repository classes, but haven't found anything useful.

How can it be done? And after that, is there any problem with changing a file's content and pushing it to github, all from the in-memory repository?

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
arikabc
  • 675
  • 1
  • 6
  • 11
  • Its because `inm.getDirectory()` returns null, so effectively you are not setting directory at all. Just ran trought this. – Antoniossss Oct 10 '18 at 07:40

3 Answers3

7

There is a proper solution without any files on disk, even temporary:

    // sample usage
    private void loadFromGit() throws Exception {
        ObjectLoader loader = loadRemote("https://github.com/msangel/promisified-resource-loader", "master", "README.md");
        loader.copyTo(System.out);
    }

    private ObjectLoader loadRemote(String uri, String branch, String filename) throws Exception {
        DfsRepositoryDescription repoDesc = new DfsRepositoryDescription();
        InMemoryRepository repo = new InMemoryRepository(repoDesc);
        Git git = new Git(repo);
        git.fetch()
                .setRemote(uri)
                .setRefSpecs(new RefSpec("+refs/heads/*:refs/heads/*"))
                .call();
        repo.getObjectDatabase();
        ObjectId lastCommitId = repo.resolve("refs/heads/"+ branch);
        RevWalk revWalk = new RevWalk(repo);
        RevCommit commit = revWalk.parseCommit(lastCommitId);
        RevTree tree = commit.getTree();
        TreeWalk treeWalk = new TreeWalk(repo);
        treeWalk.addTree(tree);
        treeWalk.setRecursive(true);
        treeWalk.setFilter(PathFilter.create(filename));
        if (!treeWalk.next()) {
            return null;
        }
        ObjectId objectId = treeWalk.getObjectId(0);
        ObjectLoader loader = repo.open(objectId);
        return loader;
    }

So, actually you have TreeWalk here (which is default to recursive), but you can change this and manually iterate all from repo.

msangel
  • 9,895
  • 3
  • 50
  • 69
  • This is a nice exmaple of getting the repo into memory. But the OP also wants to know how to change a file and commit it to repo all in memory. At least I want to know it :) – tibi Oct 12 '21 at 10:48
4

The CloneCommand will always create a file-based repository. The lines from your post that create an InMemoryRepository have no effect on the clone command.

I suggest to clone into a temporary location if you only need the repository to make a change and push the result.

For example:

Git.cloneRepository().setURI( ... ).setDirectory( new File("/path/to/empty/dir" ) ).call();  

An InMemoryRepository in JGit still requires a work directory for operations like checkout, etc. Only the object database which is usually found in the .git directory is stored in memory

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
  • If I don't set a directory to the `CloneCommand`, where is the file-based repository saved? And is the `InMemoryRepository` only used for new repositories? (which are obviously not saved) – arikabc Jul 07 '15 at 20:58
  • 2
    If no directory is set, the `CloneCommand` will choose a directory (the the current program working directory plus the repository name, see the JavaDoc). And yes, the `InMemoryRepository` in its current state is very limited in its practical use and only meant for experimental or testing purposes. – Rüdiger Herrmann Jul 07 '15 at 21:13
1

Also, with a small tweak on previous answer:

  • Listing all files in commit
  • Filtering files by extension

    public Function<String, List<String>> lastCommitFiles = (extension) -> {
    
    final List<String> paths = new ArrayList<>();
    try {
    
        ...
    
        try (TreeWalk walk = new TreeWalk(inMemoryRepository)) {
            walk.addTree(revTree);
            walk.setRecursive(true);
            walk.setPostOrderTraversal(true);
            while (walk.next()) {
                if ((extension != null && extension.length() > 0)) {
                    if (walk.getPathString().lastIndexOf(extension) != -1) {
                        paths.add(walk.getPathString());
                    }
                } else paths.add(walk.getPathString());
            }
        }
        return paths;
    } catch (GitAPIException | IOException e) {
        log.error(e.getMessage());
    }
    
    return paths;
    

    };

Felix Aballi
  • 899
  • 1
  • 13
  • 31