如何在取消引用符号链接时通过硬链接复制文件(从 gi​​t 附件中提取文件)

如何在取消引用符号链接时通过硬链接复制文件(从 gi​​t 附件中提取文件)

我正在尝试git 附件,在向 git annex 导入太多内容之前,我想弄清楚如何快速删除“附加的”git 存储库,而不会丢失附加的文件内容及其目录结构。所有“附加的”文件实际上都是符号链接到的.git/annex/objects,如下所示:

$ git init
Initialised empty Git repository in /tmp/annex/.git/
$ git annex init
init  ok
(recording state in git...)
$ touch foo
$ git annex add foo
add foo ok
(recording state in git...)
$ git commit -a
[master (root-commit) 609a6df] Initial
1 file changed, 1 insertion(+)
create mode 120000 foo
$ ls -l foo
lrwxrwxrwx 1 me me 178 Jan  6 15:10 foo -> .git/annex/objects/pX/ZJ/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

因此,简单地删除 .git 目录也会删除所有实际文件内容!

我想要的是一个命令,用于获取像上面那样的附加存储库并创建一个仅包含(未符号链接的)文件的新目录:在本例中,仅包含单个文件foo。为了在删除原始附件目录之前节省空间,我希望该文件foo是硬链接.git/annex/objects/pX/ZJ/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855(显然,在现实的例子中,存储库中会有许多文件)。

git annex 自身用于“取消附加”的命令(例如git annex uninit)非常有限:特别是它们不能很好地支持将 git annex 存储库快速转换为普通文件目录。这引发了以下问题:

如何复制文件目录,取消引用符号链接,但硬链接到符号链接的引用?

我尝试rsync--link-dest,像这样:

rsync -rLptgoD --safe-links --exclude='.git/' --link-dest=annex annex/ copy

但这不会产生硬链接,大概是因为符号链接的存在意味着 rsync 不知道要硬链接到哪些文件。

答案1

git annex 自身的“取消附加”命令(例如git annex uninit)是有限的:特别是它们不能很好地支持将 git annex 存储库快速转换为普通的文件目录。

这听起来不对。该命令以递归方式运行,因此您只需在存储库的根目录下运行git annex unannex .,即可提取所有文件。

但在 v5 格式的 git-annex 存储库中,您还有另一种选择:使用 切换到“直接”模式git annex direct。这会将所有文件移出对象存储并直接公开它们,而无需链接。此命令会一次转换整个工作树。

文件仍然由 git-annex 跟踪;v5 直接模式仅仅改变了本地签出的方式,所以它可能是最快的方法,因为它实际上并没有将新数据写入 Git。

所以如果你想克隆整个文件树,您可以 1)将存储库切换到“直接”模式;2)使用 rsync 硬链接所有文件(完全排除 .git);3)如果需要,将原始存储库再次切换到“间接”模式。

或者,切换到直接模式后,只需删除该.git文件夹...

答案2

这将快速复制文件,同时(某种程度上)保留权限、所有权、时间戳和硬链接,以免使用更多空间(只要副本在同一个文件系统上):

cp -rLlp annex copy

令我惊讶的是,rsync 无法复制这种行为,但据我所知,它不能。

cp 解决方案存在三个问题:

  1. 任何未跟踪或直接签入 git 的非附加符号链接都将被替换为它们指向的任何文件(但我怀疑我不会拥有任何文件)。在我意识到我可以使用之前cp,我编写了一个 Python 程序,用于os.walk一次复制一个文件:由于它知道 git annex,因此它会得到正确的结果:它分别运行rsync -ptgo --dirs以复制目录和cp -Llcp -Pl非目录附加文件以及其他非目录文件(该程序与实用程序代码有点纠缠,没有经过仔细测试,因此我不会在这里发布它)。

  2. 虽然cp保留了权限,但这些文件可能被“锁定”,这意味着它们处于符号链接的不可编辑状态。因此,可写文件最终在副本中将变为不可写。使用git annex unlock .代替cp将避免该问题(这适用于 git annex repo 格式 v6/v7——对于早期格式,我相信您可以切换到直接模式而不是解锁)。

  3. 您最终会.git在目录中得到目录的副本copy,其中包含相同文件的更多硬链接副本。同样,由于硬链接,它不会占用更多空间,您可以直接sudo rm -rf .git删除它。

答案3

另一个答案(在存储库的顶层目录中运行此程序):

git annex unlock .

优点:

  • 权限被保留(参见我的其他答案)。
  • 未跟踪和已解锁的符号链接被保留(再次参见我的其他答案)。
  • 运行 strace 发现至少 Joey Hess 诊断出的缓慢问题此处发表评论因为影响git annex uninit(也适用于git annex unannex .)不会影响此命令。不过,我不知道它对于实际存储库有多快:我只是在这里进行先发制人的测试。

缺点:

  • 除非您使用 Btrfs 之类的文件系统,GNU cp 支持写时复制,否则 git annex 将复制每个文件(最终,每个附加文件都会同时出现.git在工作副本下和常规文件中)。我认为这仍然不应该太慢,但会占用磁盘空间。

相关内容