将Git LFS跟踪的文件移动到常规Git下

129

我有一个项目,其中使用Git LFS来存储视频文件。现在我的构建服务器出了一些问题,还不支持Git LFS。由于这是一个外部服务,我无法真正影响构建过程,因此希望将文件从Git LFS中移回“常规”Git。我使用 git lfs untrack '<file-type>' 取消了文件类型的跟踪,但是 git lfs ls-files 仍然列出了之前添加的文件列表。

我想我可以删除这些文件,推送更改,然后手动重新添加它们,但这真的是建议的做法吗?

6个回答

176

我最近遇到了一个问题,就是有些资产被错误地添加到了不应该添加的分支上的 git-lfs 中。我的解决方案是:

git lfs untrack '<file-type>'
git rm --cached '<file-type>'
git add '<file-type>'
git commit -m "restore '<file-type>' to git from lfs"
结果是对git-lfs oid sha256指针进行重写,使用标准文件内容。
(编辑于2019-03):接受的答案已更改为提供简单情况的简单解决方案。如有更复杂的情况,请参见VonC的答案中的编辑以获取备选解决方案。

7
如果.gitattributes已正确设置以跟踪我想要的文件,这个解决方案将非常有效。比其他发布的解决方案简单得多。 - Josh Rickert
3
2018年对我有用。给其他人的提示:'file-type' 是 '**.jpg'。 - oligofren
3
虽然被标记为采纳答案让我感到高兴,但我认为重要的是指出,虽然这是对特定问题的一个好而简单的解决方案,但对于更复杂的情况,读者应该参考tstephens619和ttaylorr在VonC的回答中建议的分批方法。 - mred
2
@mred,同意。我建议编辑并链接到之前接受的答案。这个答案被埋没了,但人们发现它很有帮助,所以我觉得这应该保留为访问者看到的第一件事情。 - Olli Niskanen
1
@fencekicker 我认为这里缺少的一步是你必须已经在你的目录中拥有普通文件(而不是LFS指针)。尝试执行 git lfs checkout <file1> <file2> <file3>,然后按照这里的步骤操作。 - hackerb9
显示剩余4条评论

78

自 Git 2.16 版本(发布于2018年1月17日),您可以使用 git add 命令的 --renormalize 标志来轻松完成此操作:

git lfs untrack "<pattern>"
git add --renormalize .
git commit -m "Restore file contents that were previously in LFS"

来自Git文档:

--renormalize: 将“清洁”过程重新应用于所有已跟踪的文件,强制将其再次添加到索引中。在更改core.autocrlf配置或text属性以更正错误的CRLF/LF行结尾的文件后,这非常有用。此选项意味着-u

关键部分在于“所有已跟踪文件”。通常,仅当Git操作更改工作树中的文件时才会运行过滤器。在.gitattributes中更改LFS白名单不是Git操作,因此在运行git lfs untrack后,索引处于不一致状态。运行git add --renormalize .告诉Git重新运行每个文件上的过滤器,这可以确保所有应该在LFS中的文件都在其中,而所有不应该在其中的文件则不会被添加。


4
这种方法的好处在于,如果你从过去或另一个分支进行检出,旧的lfs内容仍然存在。但未来它将不再使用lfs。我唯一想添加的是,现在从.gitattributes中删除lfs内容(或在我的情况下删除整个文件),并将其一同提交检查。 - David Casper
3
需要注意的是,这不会改变历史记录,也就是说,如果您查看旧的提交,可能会得到指向 LFS 的指针而不是实际文件。如果您已经移动了存储库或删除了 LFS,则无法通过此方式获取那些旧的大文件。 - Thomas Tempelmann
这种方法的不好之处在于它会触及所有文件,最终你会得到一个包含所有内容的大型提交。 - BigMiner
1
双引号应该用于Windows命令行。 - Serge Sotnyk

36

Issue 641提到了相同的问题。

我试图停止使用Git LFS,但无法通过git lfs uninitgit lfs untrackgit rm等方法恢复先前跟踪的指针文件。即使我将这些文件移回原位,它们仍会在git lfs ls-files中被列为由Git LFS跟踪。如何从我的repo中退出整个Git LFS?

答案是:

  1. 使用git lfs uninit删除所有filter.lfs.* git config条目。
  2. 通过运行git lfs untrack来清除任何使用lfs过滤器的属性,或者如果LFS是您唯一使用它的内容,则删除.gitattributes

这样做后,任何添加的文件都将直接进入git。

但事实并不简单:

后来我发现在我的工作目录中出现了LFS指针文件,并不得不手动使用那些指针中存储的sha1哈希从.git/lfs中恢复所有图片。


2016年3月的更新,问题957说明了由tstephens619提出的可能解决方案:

我犯了同样的错误,将几种小型图形格式包括在我的git lfs跟踪列表中。
我通过以下步骤将这些文件移回到git中:

  • 创建一个由git-lfs当前跟踪的所有文件列表,过滤掉*.gz*.rpm(我仍然想使用git-lfs跟踪这些扩展名)

    git lfs ls-files | grep -vE "\.gz|\.rpm$" | cut -d ' ' -f 3 > ~/temp/lfs-files.txt
    
  • 停止追踪小型图形文件

  • git lfs untrack "*.tts"
    git lfs untrack "*.bfx"
    git lfs untrack "*.ttf"
    git lfs untrack "*.xcf"
    git lfs untrack "*.pkm"
    git lfs untrack "*.png"
    
  • 暂时取消 git-lfs

  • git lfs uninit
    # Git LFS 2.x+
    git lfs uninstall
    
  • 使用文件列表触碰每个文件:

  • cat ~/temp/lfs-files.txt | xargs touch
    

git status现在会显示每个文件的修改状态

  • 将更改添加到git索引中(我通过git gui完成此操作)

  • 提交更改,然后重新初始化git-lfs

git commit
git lfs init

维护者 ttaylorr 补充道:

一种实现方法是:

for file in $FILES_TO_REVERT; do
  git lfs untrack "$file";
  git rm --cached "$file";
  git add --force "$file";
done

git commit -m "..."

我的偏好是不向Git LFS添加一个命令来实现上述功能,因为使用Git和Git LFS提供的瓷器命令有许多不同的方式可以实现。


5
感谢您从项目中找到正确的问题。但即使是这个解决方案也需要手动移动文件,这似乎有点奇怪,因为从 Git 到 Git LFS 的工作流程基本上是 git rm --cached <file> -> git add <file> -> git commit,只要跟踪设置正确就可以了。 - Olli Niskanen
1
@Klipi 我同意:似乎无法跟踪的情况仍有待完善。 - VonC
1
@Klipi 好的,谢谢提醒。我会监控它。 - VonC
5
我使用的 LFS 版本(2.0.2)没有“uninit”命令。我需要使用“git lfs uninstall”命令代替“git lfs uninit”。请注意,这两个命令的含义相同,只是名称不同。 - endavid

8
编辑:使用 GIT LFS 好几年后,根据本答案的多次赞同和反对,我认为以下警告仍然适用:GIT LFS 存在很多缺陷,如难以管理应该放入 LFS 的文件、在 Windows 上(意外地)将许多小文件添加到 LFS 时存在性能问题、对多个远程和远程 URL 格式的支持有限、难以从 LFS 中删除文件、合并时可能遇到各种问题等。GIT LFS 是存在于版本树之外的 GIT 中的异类。然而,我想重新表达我的初始警告如下:
  1. 只将通常放入 GIT 中的文件(例如,您拥有并偶尔更改的“源文件”)放入 GIT LFS 中
  2. 只将大文件放入 GIT LFS 中。
  3. 如果您需要一个管理二进制依赖项的系统,请考虑使用软件包管理器。
  4. 不要使用 Subversion 替代 GIT LFS。 它更糟。
  5. 准备好搞乱你的工作目录。 在对 LFS 进行任何重大更改之前,请确保备份(推送)有价值的更改。
  6. 在合并时,始终先合并 .gitattributes。
编辑:这是我的原始答案:
从 GIT LFS 中移除任何内容都很困难,虽然这里提供的解决方案可能可行(需要修改),但它们需要大量的工作,而且可能会对您的存储库产生副作用。
如果你来到这里,现在是时候问问自己是否想要使用 GIT LFS 管理大文件以及 GIT 本身(因为它是一个分布式版本控制系统)是否是个好选择。
如果您有许多大文件并且您是单个组织在项目上工作,那么类似于 Subversion 的东西可能更适合您。

8
每次访问这个问题,都证明了我的观点。根据GIT LFS的官方文档,从LFS中删除文件应该是易如反掌的事情。但实际上并非如此,因为它不起作用。人们一直在苦苦挣扎,原因是 a) 很难精细调整要添加的文件, b) 在Windows上添加许多小文件会有性能问题,因为GIT LFS运行为子进程,而启动子进程是昂贵的,没有fork()支持。最重要的是:为了保持心理健康,请在所有这些问题解决之前不要使用LFS。这些问题已经存在多年了... - Florian Winter
这个回答更像是一个观点而不是一个答案,但是由于问题涉及在大型二进制文件上使用GIT,这更像是一个警告,我投票不删除这个答案。 - F. Hauri - Give Up GitHub

5

我在Windows上进行步骤时遇到了问题。为了删除所有git lfs跟踪的文件并还原原始文件,我在git bash中执行了以下操作:

  1. 删除.gitattributes文件

  2. git lfs ls-files | cut -d ' ' -f 3 > lfs-files.txt

  3. 执行以下代码片段:

代码片段:

while read file; do
  git lfs untrack "$file";
  git rm --cached "$file";
  git add --force "$file";
done <lfs-files.txt

“-f 3”序列是什么意思? - Francisco Maria Calisto
1
@Francisco Maria Calisto 它将 git lfs ls-files 的结果按空格分割,并取第三个条目(即文件名)。请参见 https://unix.stackexchange.com/questions/122055/what-constitutes-a-field-for-the-cut-command 以获取有关 cut -f 的更广泛解释。 - Joker

1

1
刚刚尝试了一下,重写了整个提交历史。需要提醒的是,请注意。 - crizzis
感谢@crizzis。我认为这应该是一种核选项。也许我应该在他们的文档中加入一个链接。它确实说“所有东西”,所以我觉得这足以作为警告。 - Kris Stern
你提供的链接已经失效了,但是你的解决方案对我来说是唯一有效的。 - Honza Vojtěch
@HonzaVojtěch 谢谢你告诉我!我刚刚更新了链接。 - Kris Stern

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