Java(JGIT)的Files.delete()无法删除文件,但file.delete()可以成功删除

8
我正在使用jgit(版本4.8.0.201706111038-r)将git repo克隆到临时目录,并添加关闭挂钩以在终止后删除临时目录。但是,关机挂钩无法删除.git子目录中的某些文件(尽管已经关闭了Git对象,这是jgit所需的)。有趣的是,如果我使用Path API (Files.delete(<PATH>))删除,删除只会失败一次,而不是使用旧的file.delete()
这是一个最小的独立示例,其唯一的依赖项是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;
        }
    });
}

输出:

...
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

有人能解释一下为什么会发生这种情况吗?是否有办法让JGIT正确关闭这些文件,以便Files.delete()可以正常工作?

2个回答

4
当从CloneCommand获得时,Git::close单独应该足以释放JGit为给定存储库持有的所有文件句柄。在这种情况下,它只是委托给Repository::close
我认为您看到的Files::delete()File::delete()之间的区别在这里解释了: Difference between Files#delete(Path) and File#delete()) 也许无关,但我认为还值得一提的是,最近引入的自动gc背景线程存在问题。它可能还会阻止成功删除存储库。请参见此邮件列表线程:

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

为了解决后一个问题,我在存储库的配置中禁用了自动垃圾回收,如下所示:
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();

常量从ConfigConstants静态导入。
这符合你的问题吗?

我意识到双重close()调用是多余的,我只是为了问题的目的而使调用非常明确。关于GC配置 - 我已经将您提供的代码添加到复制代码中,但我得到了相同的行为。然而,关于Files.delete()与File.delete()的解释似乎是正确的。 - Malt
在使用你提供的代码禁用自动垃圾回收后,我也没有看到任何影响,但是我认为你可能会遇到这个问题,即Files::close与File::close的问题已得到解决。 - Rüdiger Herrmann
1
@RüdigerHerrmann,恐怕这并不能解决我的问题。我在一些邮件列表中看到过关闭存储库后出现锁定文件的2或3个案例,但没有找到解决方法。 - Antoniossss
如果您在使用JGit的最新版本时遇到了这些问题,并且可以复现,请考虑提交错误报告。 - Rüdiger Herrmann

1
如果仍然遇到相同的问题,对我有用的方法是强制调用gc以释放文件锁定。
gitReference.gc().call()

这是我的完整的关闭方法:

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

祝你好运

Jalal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接