8

I'm cloning a git repo into a temp directory using jgit (version 4.8.0.201706111038-r) and adding a shutdown hook to delete the temp directory after termination. However, the shutdown hook fails to delete some files from within the .git subdirectory (despite closing the Git object, as required by jgit).

The funny thing though, is that the deletion fails only if I use the Path API (Files.delete(<PATH>)), but not if I delete using the old file.delete().

Here's a minimal standalone example whose only dependency is jgit 4.8.0.201706111038-r:

public static void main(String... args) throws Exception {
    String gitRepo = "https://github.com/netgloo/spring-boot-samples.git";
    Path localDir = Files.createTempDirectory(null);

    // Clone repo
    Git git = Git.cloneRepository().setURI(gitRepo).setBranch("master").setDirectory(localDir.toFile()).call();

    // Print some stuff to make sure that the git repo actually works
    for (RevCommit c : git.log().call()) {
        System.out.println(c);
    }

    git.getRepository().close(); // Close all the things!
    git.close(); // Close all the things!

    // Delete
    Files.walkFileTree(localDir, new SimpleFileVisitor<Path>() {
        void safeDelete(Path p) {
            try {
                Files.delete(p);
            } catch (Exception e) {
                try {
                    Files.delete(p);
                } catch (Exception e2) {
                    System.err.println("Failed to delete " + p + " due to " + e.getClass().getSimpleName() + " using Files.detlete().\nTrying toFile().delete(): " + p.toFile().delete());
                }
            }
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            safeDelete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
            safeDelete(dir);
            return FileVisitResult.CONTINUE;
        }
    });
}

Output:

...
Failed to delete C:\Users\malt\AppData\Local\Temp\7908805853015958247\.git\objects\pack\pack-9cc3ec0769e34546bb7683f4e22ef67b3c800444.idx due to AccessDeniedException using Files.detlete().
Trying toFile().delete(): true
Failed to delete C:\Users\malt\AppData\Local\Temp\7908805853015958247\.git\objects\pack\pack-9cc3ec0769e34546bb7683f4e22ef67b3c800444.pack due to AccessDeniedException using Files.detlete().
Trying toFile().delete(): true

Can anyone explain why this happens? And is there a way to get JGIT to properly close these files so that Files.delete() works?

Malt
  • 28,965
  • 9
  • 65
  • 105

2 Answers2

4

When obtained from a CloneCommand, Git::close alone should be enough to release all file handles, held by JGit for a given repository. It simply delegates to Repository::close in this case.

I think the difference between Files::delete() and File::delete() that you are seeing is explained here:

Difference between Files#delete(Path) and File#delete()

Probably unrelated, but I think nonetheless worth mentioning, is an issue with the recently introduced background thread for auto-gc. It may also prevent a repository from being deleted successfully. See this mailing list thread:

https://dev.eclipse.org/mhonarc/lists/jgit-dev/msg03370.html

To work around the latter issue, I disabled auto-gc in the configuration of the repository like this:

StoredConfig config = repository.getConfig();
config.setBoolean(CONFIG_GC_SECTION, null, CONFIG_KEY_AUTODETACH, false);
config.setInt(CONFIG_GC_SECTION, null, CONFIG_KEY_AUTOPACKLIMIT, -1);
config.setInt(CONFIG_GC_SECTION, null, CONFIG_KEY_AUTO, -1);
config.save();

The constants are imported static from ConfigConstants.

Does that match your problem?

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
  • I realize that the double `close()` call is redundant, I just wanted to make the call very explicit for the purpose of the question. Regarding the GC configuration - I've added the code you've provided to the reproduction code, but I get the same behavior. The Files.delete() vs File.delete() explanation seems to be on point though. – Malt Jul 23 '17 at 16:33
  • I also saw no effect after disabling auto-gc with your snippet, but I thought you may run into this once, the Files::close vs File::close issue is solved. – Rüdiger Herrmann Jul 23 '17 at 16:37
  • 1
    @RüdigerHerrmann This does not solves the issue I am affraid. I have seen 2 or 3 cases about locked files after closing repository on some mailing lists but found no resolution to this. – Antoniossss Nov 01 '18 at 12:26
  • If you see these problems with the latest version of JGit and can reproduce it, you may want to file a bug. – Rüdiger Herrmann Nov 01 '18 at 12:32
1

If somebody still faces the same issue, what worked for me is to force calling gc to release the files locking

gitReference.gc().call()

Here is the full close method of mine:

public void close() {
    if (gitReference != null) {
       try {            
            gitReference.close();           
            gitReference.gc().call();
        } catch (GitAPIException e) {
            throw new RuntimeException(e);
        }
        gitReference = null;
    }
}

Good Luck

Jalal

Jalal Kiswani
  • 738
  • 5
  • 11