Git-LFS 使用记录

Git-LFS 使用记录

IT, 其他

最近着手处理一个混合有 Binary 的复杂 Repository,由于 Git 本身使用 text-diff 进行差分管理,而二进制文件无法很好的通过这种方式管理,因此需要使用 Git-LFS(Git extension for versioning large files) 分开处理,在支持 Git-LFS 的服务器中,只要本地启用 Git-LFS(git lfs install) 并对需要的文件开启 LFS 追踪即可。

但这对于一个混合有 Binary 的复杂 Repository 并不是那么容易:如果对文件逐一添加追踪会消耗大量时间并且会导致 .gitattributes 文件变得特别复杂,不利于管理。那么显而易见,这里的最佳的方案是使用文件扩展名进行批量的管理,并针对不具备扩展名的文件,或者例外文件进行额外的管理。

对于实现这个目标,最大的问题在于如何快速可靠的确定文件类型。为了达到这个目的需要两个工具:

file 可以查看文件的类型,通过 file -i 还可以获取到它的 mime type 以及 charset。该命令在 Linux 或者 Windows 的 MSYS2 均存在,它通过检查文件的 magic num 确定文件类型,因此可以视为比较精确的文件识别方案。 git check-attr -a 可以查看当前文件是否被 git-lfs 追踪。git-lfs 工作时通过获取 .gitattributes 中的 filter 信息了解当前文件是否需要应用 filter: lfs

使用上述工具可以生成几个不同的列表:

  • 应当使用 git-lfs 管理的文件类型
  • 应当使用 git 管理的文件类型
  • git-lfs 的例外

file 获取到的 charset 如果是 binary 则属于应该受到 git-lfs 管理的二进制文件,对比 git check-attr -a 得到的结果,它们应当一致。

file -i 输出内容样例:mg.txt: text/plain; charset=us-ascii,使用 regex 分离信息。

我们可以用如下 Python 函数获取 mime_type 和 charset:

def get_mime_type(file_path):
    """call linux file to get mime type information
    """
    cmd = f"file -i \"{file_path}\""
    cli = shlex.split(cmd)
    result = subprocess.run(
        cli,
        capture_output=True,
        text=True,
        check=True
    )
    p = re.compile(r"(?P<filename>.*):\s(?P<mime>.*/.*);\scharset=(?P<charset>.*)")
    m = p.search(output_str)
    mime_type = ""
    charset = ""
    if m is not None:
        filename = m.group("filename")
        mime_type = m.group("mime")
        charset = m.group("charset")
    return mime_type, charset

类似的也可以获取 git check-attr -a 的信息:

def test_lfs(root_dir, file_path):
    """call git to test lfs
    """
    full_path = os.path.abspath(file_path)
    cmd = f"git check-attr -a \"{full_path}\""
    cli = shlex.split(cmd)
    result = subprocess.run(
        cli,
        cwd=root_dir,
        capture_output=True,
        text=True,
        check=True
    )
    output_str = str(result.stdout)
    if len(output_str)>0 and "filter: lfs" in output_str:
        return True
    else:
        return False

由于上述操作在运行 file 命令以及git 命令时没有 data dependence,因此完全可以使用 ThreadPoolExecutor 等简单的多线程工具将这个运行过程多线程化,即可加快整个流程。

最后只需要判断 charset 中是否存在 binary 字样即可,分别创建 text_extensionsnon_text_extensionsexclude_extensionsset,直接将相应的扩展名加入 set,通过 set 的特性保证唯一性。

值得注意的是在生成 .gitattributes 时,需要关注执行顺序,在 git 中,每一个文件最多对应该文件中的一条规则,最后一个匹配的规则决定了文件的属性。因此最佳的方案是先加载所有的扩展名列表,随后是不包含扩展名的文件列表,最后是所有的例外文件扩展名或者文件名。

使用 git-lfs 的文件使用规则 {ext} filter=lfs diff=lfs merge=lfs -text

若需要为个别文件设置例外则使用 {ext} !filter !merge !diff !text

不符合上述任意一条规则的文件将不会被 lfs 管理。

Amefs, EFS, IT
上一篇文章
vSphere 备份权限设置

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Fill out this field
Fill out this field
请输入有效的邮箱地址。

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

keyboard_arrow_up