【问题标题】:Can Git treat ZIP files as directories and files inside the ZIP as blobs?Git 能否将 ZIP 文件视为目录,将 ZIP 中的文件视为 blob?
【发布时间】:2011-12-21 13:08:09
【问题描述】:

场景

想象一下,我不得不处理一些始终存储在 .zip 文件中的文件。 ZIP 文件中的一些文件是小文本文件并且经常更改,而其他文件较大但幸运的是相当静态(例如图像)。

如果我想将这些 ZIP 文件放在 Git 存储库中,每个 ZIP 都被视为一个 blob,因此每当我提交存储库时,存储库的大小都会随着 ZIP 文件的大小而增长......即使里面只有一个小文本文件发生了变化!

为什么这是现实的

Microsoft Word 2007/2010.docx 和 Excel .xlsx 文件是 ZIP 文件...

我想要什么

有没有办法告诉 Git 不要将 ZIP 文件视为文件,而是将其视为目录并将其内容视为文件?

优势

但是你说它行不通?

我意识到,如果没有额外的元数据,这会导致一些歧义:在git checkout 上,Git 必须决定是将foo.zip/bar.txt 创建为常规目录中的文件还是 ZIP 文件。不过,我认为这可以通过配置选项来解决。

如何实现的两个想法(如果尚不存在)

  • 在 Git 中使用 minizipIO::Compress::Zip 等库
  • 不知何故添加了一个文件系统层,以便 Git 实际上将 ZIP 文件视为目录

【问题讨论】:

  • .docx 文件的场景是有意义的,但在许多其他情况下,您可能需要考虑使用 git 正常跟踪单个文件,并且只构建生成的 .zip使用适当的构建工具,例如 make
  • 考虑到看起来彼此不同的两个 zip 文件可以保存完全相同的数据(例如,使用两种不同压缩级别压缩两次的文本文件),这变得更加棘手。虽然用很少的信息很容易表示解压缩文件的两个版本之间的差异,但我想用尽可能少的信息表示两个版本的存档之间的差异(这基本上是 git 必须做的) -微不足道。
  • 您是否最终得到了Jeff's answer 或其他任何东西的实施解决方案?我想知道除了for tar archives之外基本相同,这应该会产生一个兼容的答案......
  • SAP 的信息设计工具 (IDT) 为其UNX 格式创建了类似的文件结构。它也是递归的:它包含一个BLX文件和一个DFX文件,它们都是档案,分别对应的是“业务层”和“数据基础”。我也想要一个解决方案。
  • Jetbrains 内置 VCS 确实允许您查看 zip 类型的文件。非常有用,但需要您查看,例如IDE 中的 PR。现在微软已经接管了,我们也可以在 github pr diff 中看到这一点。

标签: git zip msysgit


【解决方案1】:

这是我的方法:

  • 使用 Git diff 过滤器将存档文件替换为内容摘要

    git config filter.zip.clean "unzip -v %f | tail -n +4 | head -n -2 | awk '{ print \$7,\$8 }' | grep -vE /$ | LC_ALL=C sort -sfk 2,2"
    git config filter.zip.smudge "cat"
    git config filter.zip.required true
    
  • 使用 pre-commit 挂钩来提取和添加存档内容:

    #!/bin/sh
    #
    # Git archive extraction pre commit hook
    #
    # Created: 2021 by Vivien Richter <vivien-richter@outlook.de>
    # License: CC-BY-4.0
    # Version: 1.0.2
    
    # Configuration
    ARCHIVE_EXTENSIONS=$(cat .gitattributes | grep "zip" | tr -d [][:upper:] | cut -d " " -f1 | cut -d. -f2 | head -c -1 | tr "\n" "|")
    
    # Processing
    for STAGED_FILE in $(git diff --name-only --cached | grep -iE "\.($ARCHIVE_EXTENSIONS)$")
    do
        # Deletes the old archive content
        rm -rf ".$(basename $STAGED_FILE).content"
        # Extracts the archive content, if the archive itself is not removed
        if [ -f "$STAGED_FILE" ]; then
            unzip -o $STAGED_FILE -d "$(dirname $STAGED_FILE)/.$(basename $STAGED_FILE).content"
        fi
        # Adds extracted or deleted archive content to the stage
        git add "$(dirname $STAGED_FILE)/.$(basename $STAGED_FILE).content"
    done
    
  • 使用 post-checkout 钩子再次打包存档以供使用:

    #!/bin/sh
    #
    # Git archive packing post checkout hook
    #
    # Created: 2021 by Vivien Richter <vivien-richter@outlook.de>
    # License: CC-BY-4.0
    # Version: 1.0.0
    
    # Configuration
    ARCHIVE_EXTENSIONS=$(cat .gitattributes | grep "zip" | tr -d [][:upper:] | cut -d " " -f1 | cut -d. -f2 | head -c -1 | tr "\n" "|")
    
    # Processing
    for EXTRACTED_ARCHIVE in $(git ls-tree -dr --full-tree --name-only HEAD | grep -iE "\.($ARCHIVE_EXTENSIONS)\.content$")
    do
        # Gets filename
        FILENAME=$(dirname $EXTRACTED_ARCHIVE)/$(basename $EXTRACTED_ARCHIVE | cut -d. -f2- | awk -F '.content' '{ print $1 }')
        # Removes the dummy archive file
        rm $FILENAME
        # Jumps into the extracted archive
        cd $EXTRACTED_ARCHIVE
        # Creates the real archive file
        zip -r9 ../"$FILENAME" $(find . -type f)
        # Jumps back
        cd ..
    done
    
  • .gitattributes 文件中应用过滤器:

    # Macro for all file types that should be treated as ZIP archives.
    [attr]zip text filter=zip
    
    # Forces `LF` as line endings for text based files inside ZIP archives.
    **/*.content/** text=auto eol=lf
    
    # OpenDocument
    *.[oO][dD][tT] zip
    *.[oO][dD][sS] zip
    *.[oO][dD][gG] zip
    *.[oO][dD][pP] zip
    *.[oO][dD][mM] zip
    
    # Krita
    *.[kK][rR][aA] zip
    
    # VRoid Studio
    *.[vV][rR][oO][iI][dD] zip
    *.[fF][vV][pP] zip
    
  • .gitattributes文件添加一些二进制处理:

    # Macro for all binary files that should use Git LFS.
    [attr]bin -text filter=lfs diff=lfs merge=lfs lockable
    
    # Images
    *.[jJ][pP][gG] bin
    *.[jJ][pP][eE][gG] bin
    *.[pP][nN][gG] bin
    *.[aA][pP][nN][gG] bin
    *.[gG][iI][fF] bin
    *.[bB][mM][pP] bin
    *.[tT][gG][aA] bin
    *.[tT][iI][fF] bin
    *.[tT][iI][fF][fF] bin
    *.[sS][vV][gG][zZ] bin
    
  • .gitignore 文件中添加一些内容:

    # Auto generated LFS hooks
    .githooks/pre-push
    
    # Temporary files
    *~
    
  • 一些配置:

    1. 安装Git LFS
    2. 通过发出一次命令git lfs install 来准备 LFS。
    3. 设置 Git 过滤器。
    4. 通过发出命令git config core.hooksPath .githooks 安装挂钩。
    5. 通过发出命令.githooks/post-checkout 应用一次结帐挂钩。
    6. 通过发出命令git add -A 应用过滤器一次。

例如,请看这里:ZIP treatment for Git

已知问题

【讨论】:

  • 这些问题解决了吗?如果是这样,您能否更新版本和日期信息?
【解决方案2】:

ReZipDoc,类似于Zippey by sippey,允许使用 Git 以更好的方式处理 ZIP 文件。

工作原理

添加/提交基于 ZIP 的文件时,Rezip 会在将其添加到索引/提交之前将其解压缩并重新打包而不进行压缩。在未压缩的 ZIP 文件中,归档文件在其内容中按原样显示(以及每个文件之前的一些二进制元信息)。如果这些归档文件是纯文本文件,则此方法可以很好地与 Git 配合使用。

好处

与 Zippey 相比,Rezip 的主要优点是存储在存储库中的实际文件仍然是 ZIP 文件。因此,在许多情况下,它仍然按原样与相应的应用程序(例如 Open Office)一起工作,即使它是在没有经过重新打包压缩过滤器的情况下获得的。 /p>

如何使用

在您的系统上安装过滤器:

mkdir -p ~/bin
cd ~/bin

# Download the filer executable
wget https://github.com/costerwi/rezip/blob/master/Rezip.class

# Install the add/commit filter
git config --global --replace-all filter.rezip.clean "java -cp ~/bin Rezip --store"

# (optionally) Install the checkout filter
    git config --global --add filter.rezip.smudge "java -cp ~/bin Rezip"

通过在您的 &lt;repo-root&gt;/.gitattributes 文件中添加如下行来使用您的存储库中的过滤器:

[attr]textual     diff merge text
[attr]rezip       filter=rezip textual

# Microsoft Office
*.docx  rezip
*.xlsx  rezip
*.pptx  rezip
# OpenOffice
*.odt   rezip
*.ods   rezip
*.odp   rezip
# Misc
*.mcdx  rezip
*.slx   rezip

textual 部分是为了让这些文件在差异中实际显示为文本文件。

【讨论】:

  • 这听起来很酷!我已经有一段时间没有这个需求了,所以一直没有时间去实现一些东西,但这绝对是我会尝试的东西。
【解决方案3】:

Zippey - 使用 Git 文件过滤器的解决方案

我的解决方案是使用过滤器将 ZIP 文件“展平”为一个整体的、扩展的(可能是巨大的)文本文件。在git add/commit 期间,ZIP 文件将自动展开为这种文本格式以进行正常的文本比较,在结帐时,它会自动再次压缩。

文本文件由记录组成,每个记录代表 ZIP 文件中的一个文件。所以你可以认为这个文本文件是原始 ZIP 文件的基于文本的图像。如果 ZIP 文件中的文件确实是文本,则将其复制到文本文件中;否则,它会在复制到文本格式文件之前进行 Base64 编码。这使文本文件始终为文本文件。

虽然此过滤器不会使 ZIP 文件中的每个文件成为 blob,但文本文件会逐行映射 - 这是 diff 的单位 - 而二进制文件的更改可以通过其相应 Base64 的更新来表示。我认为这和OP想象的一样。

有关详细信息和原型代码,您可以阅读以下链接:

Zippey Git file filter

另外,感谢启发我了解此解决方案的地方: Description of how file filter works

【讨论】:

  • 此过滤器仍在开发中,如果您有任何问题或建议,请告诉我。
  • 我试过了,我认为它应该适合我。我只想在文档中添加一些内容,即必须修改文本文件列表 zippey.py 以包含您希望 zippey.py 识别为文本文件的任何文件类型。
  • 像这样的巨大文件对许多工具都不友好。我特别想到 github 50 MB 限制
  • 我不喜欢单一文件,因为它会构建一个太大而无法推送到 github (100MB) 的文件,并且不允许精细跟踪
  • 值得注意的是,您的存储库中没有 LICENSE 文件或任何等效文件。 No license = all rights reserved.
【解决方案4】:

来自Managing ZIP-based file formats in git

(注意:根据Ruben 的评论,这只是为了获得适当的差异,而不是提交解压缩文件。)

打开你的 ~/.gitconfig 文件(如果不存在则创建)并添加 以下节:

[diff "zip"] textconv = unzip -c -a

它的作用是使用“unzip -c -a FILENAME”来转换你的压缩文件 转换成 ASCII 文本(unzip -c 解压到 STDOUT)。接下来的事情是 创建/修改文件 REPOSITORY/.gitattributes 并添加以下内容

*.pptx diff=zip

告诉 git 使用配置中的 zip-diffing 描述 对给定掩码进行数学运算的文件(在这种情况下,所有内容都以 .pptx)。现在 git diff 会自动解压缩文件并比较 ASCII 输出比“二进制文件不同”要好一些。 另一方面,对于相应的 XML pptx 文件是,它并没有多大帮助,但对于 ZIP 文件,包括 文本(例如源代码档案)这实际上是相当的 方便。

【讨论】:

  • 这只是为了获得适当的差异,而不是提交解压缩文件..
  • 谢谢。这回答了我想解决的问题,即在git diffing 时显示我的 gzip 文件中的文本文件的更改。我使用了[diff "gzip"] = zcat*.gz diff=gzip
【解决方案5】:

应用程序的预压缩文件通常会出现问题,因为他们希望 ZIP 压缩方法和文件顺序是他们选择的一种。我相信OpenOffice .odf 文件有这个问题。

也就是说,如果您只是使用任何旧的 ZIP 文件作为将东西放在一起的方法,那么您应该能够创建一些简单的别名,以便在需要时解压缩和重新压缩。最新的 MSysGit(又名 Git for Windows)现在在 shell 代码端同时具有 zip 和 unzip,因此您可以在别名中使用它们。

我目前正在处理的项目使用 ZIP 文件作为主要的本地版本控制/存档,因此我也在尝试获得一组可行的别名,用于将这数百个 ZIP 文件吸入 Git(并将它们取出再次 ;-) 让同事们开心。

【讨论】:

  • 我刚刚对 Word 2010 做了一些测试 - 它似乎很宽容(deflate 不同的字大小,deflate64 和更改 7zip 生成的 zip 文件中的文件顺序都没有抛出 Word离开)。关于使用别名,我希望避免任何额外的手动步骤......目前我的大部分提交都通过 TortoiseGit。
【解决方案6】:

这不存在,但它可以很容易地存在于当前框架中。正如 Git 在执行 diff 时显示二进制或 ASCII 文件的行为不同一样,可以通过配置界面告诉它对某些文件类型提供特殊处理。

如果您不想更改代码库(尽管您的想法很酷),您也可以自己编写脚本,使用 pre-commit and post-checkout hooks 解压缩并存储文件,然后返回结帐时将它们转换为 .zip 状态。您必须将操作限制为仅由git add 指定的那些文件 blob/索引。

无论哪种方式都需要一些工作——这只是其他 Git 命令是否知道正在发生的事情并能正常运行的问题。

【讨论】:

  • Hooks 似乎是一个很好的研究方向;我简单地考虑了一下,但不确定它是否可以工作。 pre-commit hook 既可以修改文件系统,也可以修改暂存区?
  • @Jonas 你有没有这样做过,你有机会发布一个可行的解决方案吗?我很想有效地跟踪 git 中电子表格的更改,而 CSV 不适合我们的目的。
  • 请注意,使用在将归档文件提交到存储库之前解压缩归档文件并在签出时再次压缩文件的脚本,提交后立即签出可能会修改归档文件,即使文件存储在存档中的内容将保持不变。
  • 我刚刚写了一些钩子来做到这一点。仍在处理粗糙的边缘,但可能会有所帮助:github.com/ckrf/xlsx-git
【解决方案7】:

使用bup(详见GitMinutes #24

它是唯一设计用于处理大型(甚至非常非常大)文件的类 git 系统,这意味着每个版本的 zip 文件只会增加其增量的 repo(而不是完整的附加副本)

结果是一个实际的 git repo,一个常规的 Git 命令可以读取。

我在“git with large files”中详细说明了 bup 与 Git 的不同之处。


任何其他解决方法(如 git-annex)都不能完全令人满意,详见“git-annex with large files”。

【讨论】:

  • 这似乎非常适用于非常大的文件,该场景更适用于 XML,例如 docx 和 xlsx(通常相当小)压缩。使用 bup 会获得更小的 repo 大小,但你会区分 XML 中的实际变化吗?
  • @Ruben 这适用于大小或数量较大的文件。但它与 git 在 diff 方面并没有太大区别。
  • 看起来很有趣,但你可以将它与你的实际 git repo 一起使用吗?
  • @kutschkem 我不这么认为:bup repo 是 git repo (raw.githubusercontent.com/bup/bup/master/DESIGN),但反过来似乎并非如此。
猜你喜欢
  • 1970-01-01
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-30
  • 2012-06-16
相关资源
最近更新 更多