DataLad 是做什么的?
概述
DataLad 是建立在 Git 和 git-annex 之上的数据管理层。Git 负责跟踪数据集结构、小型文本文件和历史记录。git-annex 跟踪大文件的内容,将每个文件存储为一个键,并将数据字节保存在本地仓库不一定存在的某个地方。
这种分离是理解 DataLad 最核心的一点,因为它意味着一个新克隆的数据集包含了完整的历史记录和完整的文件列表,但几乎不包含任何数据。一个 100 TB 的数据集可以在几秒内克隆完成,占用几兆字节。数据字节仅在需要时才通过 datalad get 按文件获取。
DataLad 添加的第二件事是来源追踪。datalad run 执行一个命令,并将结果连同命令、输入和输出的机器可读记录一起提交。datalad rerun 读取该记录并重新执行它。这会将“这个图是怎么生成的”从考古问题转变为一条命令。
何时使用 DataLad 而非纯 Git
当满足以下任一条件时,请使用 DataLad:
- 文件对于 Git 来说太大,处理起来不方便,或者总量超过了每个协作者愿意存储在磁盘上的大小。
- 数据存在于多个地方(实验室服务器、集群临时存储、S3、超级计算机),并且您需要知道有哪些副本存在。
- 分析必须可重新执行,而纯提交消息不足以作为证据。
- 您正在使用来自 OpenNeuro、DANDI 或
datasets.datalad.org的已发布数据集,这些数据集以 DataLad 数据集形式分发。 - 项目内部嵌套了其他数据集,并且您希望每个数据集保持自己独立的历史记录。
当仓库仅包含代码和文本,所有内容都能舒适地放入 Git,且没有人需要部分检出时,请使用纯 Git。在一个小型纯代码仓库之上使用 DataLad 会增加不必要的间接层,而没有任何好处。
安装
# git-annex 不是用 Python 编写的,但如果您已经安装了 git 本身,可以从 PyPI 获取:
uv pip install git-annex
# 您也可以先从系统安装
# (Debian/Ubuntu: apt install git-annex; macOS: brew install git-annex;
# conda-forge: conda install -c conda-forge git-annex)
uv pip install datalad
uv pip install datalad-container # 仅用于 containers-run
datalad wtf --section dependencies # 确认 git-annex 版本可见
PyPI 的 git-annex 包为 Linux、macOS 和 Windows 提供了预编译二进制的 wheel,而不是从 Haskell 源码构建,因此它可以像任何其他 Python 依赖项一样安装,并可以在与 DataLad 相同的环境中固定版本。它不附带 git。
datalad wtf 打印已解析的环境,当行为看起来不可能时,这是首先要运行的东西。一个过时或缺失的 git-annex 是造成大量令人困惑的错误的背后原因。
DataLad 本身采用 MIT 许可证。git-annex 是一个独立的工具,采用 AGPL 许可证,这仅在您重新分发修改后的 git-annex(而非调用它)时才重要。
首先会遇到的失败:指针不是数据
执行 datalad clone 后,被 annex 管理的文件作为符号链接存在于 .git/annex/objects/ 中(或者在没有符号链接的平台上,例如 Windows 或受限文件系统,作为小型指针文件存在)。此时尚未下载任何内容。
datalad clone https://github.com/OpenNeuroDatasets/ds000001.git
cd ds000001
ls sub-01/anat/ # 文件被列出
python -c "import nibabel; nibabel.load('sub-01/anat/sub-01_T1w.nii.gz')" # 失败
datalad get sub-01/anat/sub-01_T1w.nii.gz # 现在可以了
需要识别的故障模式:工具报告文件为空、被截断、损坏、“不是 gzip 文件”或符号链接损坏,并且磁盘上的文件大小只有几百字节。那是一个指针文件,而不是损坏的下载。在读取数据之前运行 datalad get,并将“文件存在”视为其内容已存在的不足够证据。
在分析触及某个目录之前,显式地获取它:
datalad get sub-01/ # 某个路径下的所有内容
datalad get -r . # 所有内容,包括子数据集
datalad get -n -r . # 仅子数据集结构,不包含文件内容
datalad status --annex 报告本地存在多少内容,而 git annex whereis <path> 报告哪些仓库持有一个给定文件。whereis 读取记录的状态,不联系远程,因此它告诉您 git-annex 上次了解到的信息,而不是此时此刻的真实情况。
有关查找数据集、子数据集行为、安全删除内容以及修复数据集的详细信息,请参阅 data-access.md。
使用 datalad run 记录来源
在方法学背景下,datalad run 是使用 DataLad 的理由。它将命令与其效果一起保存,放在同一个提交中:
datalad run -m "extract brain mask" \
--input "sub-01/anat/sub-01_T1w.nii.gz" \
--output "derivatives/sub-01_brain.nii.gz" \
"bet {inputs} {outputs} -m"
各部分的作用以及跳过它们会带来的问题:
--input在运行前检索内容,因此命令不会因指针而失败。它还记录了依赖关系,这使得rerun可以在不同的机器上获取相同的输入。--output首先解锁或删除目标文件,这样 git-annex 就不会拒绝写入它正在保护的内容。没有它,再次运行相同的命令通常会因为权限错误而失败,因为议程文件看起来是只读的。{inputs}和{outputs}会展开为那些值。{pwd}、{dspath}和{tmpdir}也可用,{inputs[0]}可以索引单个条目。- 提交消息在
=== Do not change lines below ===和^^^ Do not change lines above ^^^之间携带一个 JSON 运行记录。请勿手动编辑该块;rerun会解析它。
当数据集有未保存的修改时,datalad run 会拒绝启动,因为不干净的起始状态会使记录不可靠。请先保存或放弃修改,或者传递 --explicit 来声明列出的输入和输出是完整的故事。在提交前使用 --dry-run basic 或 --dry-run command 检查命令。
不产生任何更改的运行不会产生提交,这与 datalad save 的行为完全相同。
重新执行
datalad rerun # 重做在 HEAD 记录的运行
datalad rerun --report # 显示将要执行的操作,不更改任何内容
datalad rerun --script recompute.sh # 提取命令而不是运行它们
datalad rerun --since <commit> -b check <revision> # 将某个范围重放到一个新分支上
将重放重定向到一个分支(-b)是测试可重现性的安全方法:重放会落在其他地方,与原始分支的 diff 会回答输出是否完全相同。
容器
借助 datalad-container 扩展,注册一次镜像,后续每次运行都会记录是哪个镜像产生了输出:
datalad containers-add fsl --url docker://brainlife/fsl:6.0.4
datalad containers-run -n fsl -m "brain mask in container" \
--input "sub-01/anat/sub-01_T1w.nii.gz" \
--output "derivatives/sub-01_brain.nii.gz" \
"bet {inputs} {outputs} -m"
镜像本身也在数据集中被跟踪,因此软件环境随数据和来源记录一起传输,而不是存在于某人的 shell 历史中。当只配置了一个容器时,可以省略 -n。
有关 STAMPED 原则和 YODA 项目布局、运行记录格式、--explicit 和 --assume-ready 语义,以及向 W3C PROV 导出来源的详细信息,请参阅 provenance.md。
保存和检查更改
datalad status # 发生了什么变化,包括子数据集状态
datalad save -m "add QC report" path/to/file
datalad save -m "checkpoint" -r # 递归进入子数据集
datalad save -m "small text file" --to-git notes.md
datalad save 根据数据集的 .gitattributes,逐个文件决定内容是进入 Git 还是 git-annex。使用 --to-git 强制将文件放入 Git,这对于应该保持直接可读的代码和小型文本文件来说是正确选择。yoda 程序(datalad create -c yoda)会自动为 code/、README.md 和 CHANGELOG.md 设置此项。
创建数据集
datalad create my_dataset # 普通数据集
datalad create -c yoda my_analysis # 分析布局(code/ 由 Git 跟踪,
# README.md 和 CHANGELOG.md 已预配置)
datalad create -d . inputs/raw # 在现有数据集下注册一个新的子数据集
-c yoda 应用 provenance.md 中描述的分析项目布局。-d . 是将新数据集注册为父数据集的子数据集,而不是在其中留下一个无关的仓库。
发布
一个 DataLad 数据集通常同时发布到两个地方:一个用于历史记录的 Git 托管服务,和一个用于 annex 管理内容的存储远程。
datalad create-sibling-github myaccount/mydataset
git annex initremote store type=S3 bucket=my-bucket encryption=none autoenable=true
datalad siblings configure -s github --publish-depends store
datalad push --to github
Git 兄弟和存储兄弟是有意通过不同工具创建的。Git 兄弟是一个 Git 远程,而 datalad create-sibling-* 处理托管服务的那些。S3 存储桶(或 WebDAV、SSH 目录)是一个 git-annex 特殊远程,而不是 Git 远程,因此使用 git annex initremote 创建。datalad siblings 之后会拾取该特殊远程并将其视为任何其他远程。此处使用 datalad siblings add --url s3://... 是本段旨在防止的错误:--url 是 Git 远程 URL,S3 不是,这会导致下面的 push --to github 在 --publish-depends 跳转时失败。
--publish-depends 的作用是防止常见的发布不完整问题:一个 Git 仓库的历史引用了从未上传的内容,因此协作者成功克隆,但随后发现每个 datalad get 都会失败。声明依赖关系使得存储兄弟每次都首先发布。
datalad push 发送 Git 历史记录,并且默认情况下(--data auto-if-wanted),也发送目标配置为需要的 annex 内容。传递 --data anything 以推送所有内容,无论目标偏好如何。
有关 RIA 存储、特殊远程、凭据处理以及配置每个兄弟持有何物的详细信息,请参阅 publishing.md。
释放磁盘空间
git annex whereis sub-01/ # 首先确认存在另一个副本
datalad drop sub-01/ # 移除本地内容,保留指针
datalad drop --what all --reckless kill <path> # 最后的手段,会销毁数据
默认情况下,datalad drop 在无法验证存在内容的另一个副本时会拒绝,这是一个安全检查,而不是障碍。--nocheck 和 --if-dirty 已被弃用;现在的写法是 --reckless availability,其含义就像字面意思一样。--what 在 filecontent(默认)、allkeys、datasets 和 all 之间选择。
值得了解的故障模式
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 文件读起来为空、被截断或为损坏的符号链接 | 内容未被检索;仅存在指针 | datalad get <path> |
| 写入现有输出时出现“Permission denied” | git-annex 写保护了 annex 管理的内容 | 使用 --output 声明,或 datalad unlock <path> |
datalad run 拒绝启动 | 数据集有未保存的更改 | 先 datalad save,或传递 --explicit |
datalad drop 拒绝 | 没有已验证的内容第二副本 | 先推送到一个兄弟,或接受 --reckless availability |
协作者克隆成功,但每个 get 都失败 | 历史记录发布时没有附带内容 | 发布存储兄弟,并设置 --publish-depends |
| 克隆成功,子数据集目录为空 | 子数据集默认未安装 | datalad get -n -r .,然后 get 您需要的路径 |
| 命令行为异常 | git-annex 缺失或太旧 | datalad wtf --section dependencies |
详细参考资料
- data-access.md:查找已发布数据集 (
registry.datalad.org、OpenNeuro、DANDI、datasets.datalad.org和///快捷方式),克隆和获取选项,子数据集处理,annex 内容状态,删除和移除,以及fsck修复。 - provenance.md:STAMPED 原则和 YODA 布局,运行记录格式,完整的
run和rerun选项,containers-run,以及将 DataLad 来源导出到 W3C PROV 的当前状态。 - publishing.md:兄弟及其操作,
create-sibling-*变体,RIA 存储,特殊远程,push语义,以及凭据处理。
相关技能
bids 技能涵盖了脑成像数据结构(BIDS),通过 DataLad 分发的大多数神经影像数据集都按此结构组织。一个典型的工作流程是使用 DataLad 克隆一个 BIDS 数据集,使用 BIDS 工具进行验证,然后在 datalad containers-run 下运行一个 BIDS-App,以便派生数据带有来源信息。
主要来源
- DataLad 文档:https://docs.datalad.org/en/stable/
- DataLad 手册:https://handbook.datalad.org/en/latest/
datalad run章节:https://handbook.datalad.org/en/latest/basics/101-108-run.html- YODA 原则:https://handbook.datalad.org/en/latest/basics/101-127-yoda.html
- STAMPED 原则(从 YODA 操作化):https://stamped-principles.org
- datalad-container:https://docs.datalad.org/projects/container/en/stable/
- git-annex:https://git-annex.branchable.com/
- 数据集注册中心:https://registry.datalad.org
致谢
本技能的主题范围部分参考了 @bcmcpher 的 MIT 许可证项目 [datalad-cli](https://github.com/bcmcpher/my-…
[因翻译截断]