如何清理Maven仓库中的旧依赖项?

100

我在.m2文件夹中有太多存储下载依赖项的Maven文件。 是否有办法清理所有旧的依赖项? 例如,如果有一个带有3个不同版本(1、2和3)的依赖项,在清理后必须只剩下第3个版本。 我如何为.m2文件夹中的所有依赖项做到这一点?


4
只需删除.m2repository文件夹即可,项目编译后会自动重新创建。 - user2339071
3
或者购买更大的硬盘,不必担心 :) - smajlo
15
也许有比等待编译和花钱买硬盘更优雅的解决方案?:) 但说真的,我在虚拟机上远程工作,所以磁盘空间(小)和编译时间(长)非常重要。这就是为什么我不能只是更换硬盘或处理器。因此,我需要找到一种更有效地使用它的方法。 - Cherry
如果你同时打开了IDE和所有最近的项目,文件系统锁定将阻止你删除正在使用的JAR包。 - digital illusion
如何通过pom文件清理依赖项? - Tushar Banne
.m2repository文件夹位于哪里? - S Gaber
10个回答

53

如果你使用的是Unix系统,你可以利用文件的访问时间。只需启用文件系统的访问时间,然后对所有需要保留依赖关系的项目进行干净的构建,最后执行以下操作(未经测试!):

find ~/.m2 -amin +5 -iname '*.pom' | while read pom; do parent=`dirname "$pom"`; rm -Rf "$parent"; done

这将查找所有最近五分钟内未访问的 *.pom 文件(假设您的构建在最多 5 分钟前启动),并删除它们所在的目录。

在 rm 前添加 "echo " 以进行“干运行”。


22
在OSX上(GNU工具也可以),使用以下命令:find ~/.m2/repository/ -atime +30 -iname '*.pom' -print0 | while read -d '' -r pom; do echo rm -rf "$(dirname $pom)"; done,其中的atime表示天数(与分钟级别的amin相比)。注意不要更改原意,但需要使内容更加通俗易懂。 - bric3
2
我使用 find ~/.m2 -atime +1w -iname '*.pom' | while read pom; do parent=$(dirname "$pom"); rm -rf "$parent"; done 在我的OSX上运行良好。在其他Unix系统上也应该可以正常工作 :) - Meeh
2
我刚刚迷上了“find”这个函数。 - Jonas Eicher

41
给定一个Maven项目的POM文件,可以使用Apache Maven Dependency Plugin从本地仓库(默认为~/.m2/repository)中删除所有依赖项。
该插件包含dependency:purge-local-repository功能,可将项目依赖项从本地仓库中删除,并可选择重新解析它们。
要清除本地依赖项,只需使用可选参数reResolve并将其设置为false,因为默认情况下它设置为true。
以下命令行调用应该有效:
mvn dependency:purge-local-repository -DreResolve=false

4
好的 :) 但是这个清理依赖只针对当前项目,不包括整个代码库。 - Cherry
3
没错!为了清理整个代码库,我会手动删除./m2/repository目录下的文件夹,就像之前评论中提到的那样,或者在新版本的Nexus(2.6.4-02之后)中,他们提供了预定任务[链接](http://blog.sonatype.com/2009/09/nexus-scheduled-tasks/#.VGUD52NGWdA),可以自带“从仓库中删除发布”的功能。这也可能很有用。 - Juanjo Marron

37
简短回答 - 在{user.home}中删除.m2文件夹。例如,在Windows 10中,用户主目录为C:\Users\user1。使用mvn clean package重新构建您的项目。只有那些项目所需的依赖项才会保留。 详细回答 - .m2文件夹就像一个普通的文件夹,其内容由不同的项目构建。我认为没有自动确定哪个库是“旧”的方法。实际上,“旧”是一个模糊的词。使用以前版本的库的原因可能有很多,因此确定哪个未使用是不可能的。
你能做的所有事情都是删除.m2文件夹,并重新构建所有项目,然后该文件夹将自动构建所有所需的库。
如果您关注仅在所有项目中使用特定版本的库,则重要的是项目的POM也应更新到最新版本。也就是说,如果不同的POM引用了库的不同版本,则会下载所有这些版本到.m2中。

因此,确定哪个是未使用的是不可能的。我不需要这种确定,我只需要保留新版本。 - Cherry
3
然后删除.m2文件夹,确保所有项目的pom.xml中只有新版本的jar条目。重新构建项目。.m2文件夹将只保留最新版本。 - Gyanendra Dwivedi
1
删除 .m2 会导致删除所有依赖项并从存储库下载新的依赖项,这非常缓慢。 - Cherry
针对所述问题 - 一个解决方案适用于所有情况 - 我们可以采用上述方式。对于速度慢的问题,我建议项目应咨询本地仓库(一种在组织中设置的生产存储库);如果没有可用的,则从公共存储库下载。建议在某个时候将构件上传到本地存储库 - 如果它在所有项目中都被广泛使用。 - Gyanendra Dwivedi

9
  1. Download all actual dependencies of your projects

    find your-projects-dir -name pom.xml -exec mvn -f '{}' dependency:resolve
    
  2. Move your local maven repository to temporary location

    mv ~/.m2 ~/saved-m2
    
  3. Rename all files maven-metadata-central.xml* from saved repository into maven-metadata.xml*

    find . -type f -name "maven-metadata-central.xml*" -exec rename -v -- 's/-central//' '{}' \;
    
  4. To setup the modified copy of the local repository as a mirror, create the directory ~/.m2 and the file ~/.m2/settings.xml with the following content (replacing user with your username):

    <settings>
     <mirrors>
      <mirror>
       <id>mycentral</id>
       <name>My Central</name>
       <url>file:/home/user/saved-m2/</url>
       <mirrorOf>central</mirrorOf>
      </mirror>
     </mirrors>
    </settings>
    
  5. Resolve your projects dependencies again:

    find your-projects-dir -name pom.xml -exec mvn -f '{}' dependency:resolve
    
  6. Now you have local maven repository with minimal of necessary artifacts. Remove local mirror from config file and from file system.


尝试了这个方法,仍然得到以下错误信息:"当前项目和插件组[org.apache.maven.plugins, org.codehaus.mojo]中都找不到前缀为'dependency'的插件,在可用的存储库[local (/home/user/.m2/repository),mycentral (file:/home/user/saved-m2/)]中也没有。" - Robert Mikes

8

这个问题已经被提出六年以上了,但我仍然没有找到任何一个能够满意地清理我的存储库的工具。因此,我自己用Python编写了一个工具来摆脱旧的本地文件。也许对其他人也有用:

repo-cleaner.py:

from os.path import isdir
from os import listdir
import shutil
import semver

import Constants

# Change to True to get a log of what will be removed
dry_run = False


def check_and_clean(path):
    files = listdir(path)
    only_files = True
    for index, file in enumerate(files):
        if isdir('/'.join([path, file])):
            only_files = False
        else:
            files[index] = None
    if only_files:
        return

    directories = [d for d in files if d is not None]
    latest_version = check_if_versions(directories)
    if latest_version is None:
        for directory in directories:
            check_and_clean('/'.join([path, directory]))
    elif len(directories) == 1:
        return
    else:
        print('Update ' + path.split(Constants.m2_path)[1])
        for directory in directories:
            if directory == latest_version:
                continue
            print(directory + ' (Has newer version: ' + latest_version + ')')
            if not dry_run:
                shutil.rmtree('/'.join([path, directory]))


def check_if_versions(directories):
    if len(directories) == 0:
        return None
    latest_version = ''
    for directory in directories:
        try:
            current_version = semver.VersionInfo.parse(directory)
        except ValueError:
            return None
        if latest_version == '':
            latest_version = directory
        if current_version.compare(latest_version) > 0:
            latest_version = directory
    return latest_version


if __name__ == '__main__':
    check_and_clean(Constants.m2_path)

Constants.py(编辑以指向您自己的本地Maven存储库):

# Paths
m2_path = '/home/jb/.m2/repository/'

请确保已安装Python 3.6+,并且已在全局环境或使用venv安装了semver包(如果缺少,请使用pip install semver进行安装)。

使用python repo-cleaner.py运行脚本。

它会在您配置的本地Maven仓库(通常为~/.m2/repository)中递归搜索,如果找到一个包含不同版本的目录,则会将所有旧版本保留最新版本。

比如说,在本地的Maven仓库中有以下树形结构:

.
└── antlr
    ├── 2.7.2
    │   ├── antlr-2.7.2.jar
    │   ├── antlr-2.7.2.jar.sha1
    │   ├── antlr-2.7.2.pom
    │   ├── antlr-2.7.2.pom.sha1
    │   └── _remote.repositories
    └── 2.7.7
        ├── antlr-2.7.7.jar
        ├── antlr-2.7.7.jar.sha1
        ├── antlr-2.7.7.pom
        ├── antlr-2.7.7.pom.sha1
        └── _remote.repositories

然后脚本会移除版本为2.7.2的antlr,剩下的是:
.
└── antlr
    └── 2.7.7
        ├── antlr-2.7.7.jar
        ├── antlr-2.7.7.jar.sha1
        ├── antlr-2.7.7.pom
        ├── antlr-2.7.7.pom.sha1
        └── _remote.repositories

任何旧版本,即使是您正在使用的版本,也将被删除。它可以轻松地通过Maven(或其他管理依赖关系的工具)恢复。
您可以通过将dry_run = True设置为不实际删除就获取要删除的日志。输出将如下所示:
    update /org/projectlombok/lombok
    1.18.2 (newer version: 1.18.6)
    1.16.20 (newer version: 1.18.6)

这意味着 lombok 的 1.16.20 版本和 1.18.2 版本将被删除,只有 1.18.6 版本将保留。
这些文件的最新版本可以在我的 github 上找到:链接

@ᴠɪɴᴄᴇɴᴛ 是的,这个脚本非常简化,因为它只在一个场景中使用。感谢您在拉取请求中对其进行了改进,既然这是您的工作,您是否也想编辑答案呢? - Andronicus

5
我创建了一个实用程序并将其托管在GitHub上,以清理本地Maven仓库中的旧版本库。该实用程序默认情况下会删除所有旧版本的构件,只保留最新版本。可以选择性地删除所有快照,源代码,Java文档,并且还可以在此过程中强制/排除组或构件。这个跨平台工具还支持基于上次访问/下载日期的日期删除。
您可以通过以下链接访问该实用程序:https://github.com/techpavan/mvn-repo-cleaner

我已经在macOS上尝试了这个工具,但似乎不能正常工作。它没有按照“accessedBefore”或者“downloadedBefore”进行过滤。 - ejboy

1

我花了几个小时研究这个问题和答案,很多答案都依赖于atime(UNIX系统上的最后访问时间),但这种解决方案不可靠,有两个原因:

  1. 大多数UNIX系统(包括Linux和macOS)最多也只会不规则地更新atime,这是有原因的:完整实现atime将意味着整个文件系统都会变慢,因为必须在每次读取文件时更新(即写入磁盘)atime,而且如此频繁的更新会非常快地磨损现代高性能SSD驱动器
  2. 在CI/CD环境中,用于构建Maven项目的VM将从共享存储中恢复其Maven存储库,这将使atime设置为“最近”的值
我因此创建了一个Maven仓库清理工具,并在https://github.com/alitokmen/maven-repository-cleaner/上提供了它。bash脚本maven-repository-cleaner.sh有一个函数cleanDirectory,它是一个递归函数,遍历~/.m2/repository/并执行以下操作:

  • 当子目录不是版本号时,它会深入该子目录进行分析
  • 当一个目录有子目录似乎是版本号时,它只删除所有较低版本

实际上,如果您有这样的层次结构:

  • artifact-group
    • artifact-name
      • 1.8
      • 1.10
      • 1.2

... maven-repository-cleaner.sh 脚本将会执行以下操作:

  1. 进入 artifact-group 目录
  2. artifact-group 目录中,进入 artifact-name 目录
  3. artifact-name 目录中,删除子文件夹 1.81.2,因为 1.101.21.8 都要好

因此,这与AndronicusPavan Kumar提供的解决方案非常相似,不同之处在于这个脚本是用Shell编写的。要在您的CI/CD平台(或任何其他形式的UNIX系统)上运行该工具,只需在构建开始或结束时使用下面的三行代码:

wget https://raw.githubusercontent.com/alitokmen/maven-repository-cleaner/main/maven-repository-cleaner.sh
chmod +x maven-repository-cleaner.sh
./maven-repository-cleaner.sh

1
我想从我的Maven存储库中删除旧的依赖项。我考虑只运行Florian的答案,但我想要一些东西,我可以反复运行而不必记住长长的Linux代码片段,并且我想要具有一定可配置性的东西——更像一个程序,而不是一系列Unix命令,所以我采用了基本思想,并将其制成了(相对较小的)Ruby程序,该程序根据最后访问时间删除旧的依赖项。
它不会删除“旧版本”,但由于您可能实际上有两个不同的活动项目,其中包含依赖项的两个不同版本,因此无论如何都不会做我想要的事情。相反,与Florian的答案类似,它会删除最近未被访问的依赖项。
如果您想尝试它,可以:
  1. 访问GitHub存储库
  2. 克隆存储库或下载源代码
  3. 可选地检查代码以确保它不是恶意软件
  4. 运行bin/mvnclean
有覆盖默认Maven存储库、忽略文件、设置阈值日期的选项,但您可以在GitHub的README中阅读这些内容。

我可能会在完成更多工作后将其打包为Ruby gem,这将简化问题(gem install mvnclean; mvnclean) 如果您已经安装并运行了Ruby。


很高兴听到这个消息。我想我应该去把它做成一个 gem。 - Geoffrey Wiseman
看来我说得太早了。我不知道规律 - 但是有些 jar 包被删除了,尽管我最近使用过它们。但与此同时,有些仍然存在。 - catholicon
嗯,如果您能提供更具体的信息,我很乐意调查一下。如果您能做到这一点,请在GitHub上为我跟踪提交问题。对我来说,它似乎运行得相当一致,但可能有一些特定于Cygwin的问题需要我去研究。您是否在同意之前查看了它输出的列表,或者列表太长而不值得审核?如果您再次尝试,将最后使用日期添加到摘要中是否有帮助?(https://github.com/geoffreywiseman/mvnclean/issues/10) - Geoffrey Wiseman
可以添加一些选项来转储调试输出(jar:最后访问日期)...我已经订阅了您打开的问题。 - catholicon
更新了;请查看问题,描述了一些您的选项。 - Geoffrey Wiseman
显示剩余2条评论

1

只需清理.m2-->repository文件夹下的所有内容。当您构建项目时,所有依赖项都会加载到这里。

在您的情况下,可能您的项目早期使用了任何依赖项的旧版本,现在版本已升级。因此最好清理.m2文件夹并使用mvn clean install构建项目。

现在,具有最新版本模块的依赖项将下载到此文件夹中。


2
除非您手动将旧版JAR文件添加到存储库中,或者依赖项在互联网上不再可用。否则这个答案有点危险...至少先备份! - Jose Manuel Gomez Alvarez

0

你需要复制项目所需的依赖项。 拿到这些后,请清除嵌入在<dependencies>标签中的所有<dependency>标签, 从你的项目的POM.XML文件中。

保存文件后,你将不会在Libraries中看到Maven Dependencies。 然后,请粘贴之前复制的那些<dependency>

所需的JAR包将由Maven自动下载,你也可以在保存文件后的生成的Maven DependenciesLibraries中看到。

谢谢。


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