Felix Hoffstaedter
Research Centre Jülich, Germany
Written by Linus Torvals, 2005
"the stupid content tracker" (manual)
What is “version control”, and why should you care?
Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later.
Distributed Version Control System (book)
Every CLONE is really a full backup of all the data
Features of git:
can revert anything you did (wrong)
lets you travel in time
tells you differences between versions
shows anything new
link datasets/repos via gitmodules
Industry standard - pre-installed on every system
git only likes text
git doesn't like binaries or large data
git annex - Joey Hess, 2010
distributed file synchronization system
git only likes text
git doesn't like binaries or large data
git annex - Joey Hess, 2010
distributed file synchronization system
"the stupid content tracker"
Distributed Version Control Systems
Every clone is really a full backup of all the data
code: git repo
derivatives: computed stuff
figures: pdf
input: raw BIDS
stats: QC etc
$ tree project
project
├── code
│ └── do_stuff.sh
├── derivatives
│ └── ROI_sub-01_T1w.json
├── figures
│ └── catreport_sub-01_T1w.pdf
├── input
│ └── sub-01_T1w.nii.gz
└── stats
└── cat_sub-01_T1w.xml
# make project a Datalad dataset
$ datalad create --force .
create(ok): /home/fhoffstaedter/TMP_DATA/OHBM-Edu_research-life-cycle/project (dataset)
# ignore input folder
$ echo "input" > .gitignore
# add all content to dataset
$ datalad save -m "backup project" -d .
add(ok): code (dataset)
add(ok): .gitmodules (file)
add(ok): .gitignore (file)
add(ok): derivatives/ROI_sub-01_T1w.json (file)
add(ok): figures/catreport_sub-01_T1w.pdf (file)
add(ok): stats/cat_sub-01_T1w.xml (file)
save(ok): . (dataset)
action summary:
add (ok: 6)
save (ok: 1)
$ tree project
project
├── code
│ └── do_stuff.sh
├── derivatives
│ └── ROI_sub-01_T1w.json
├── figures
│ └── catreport_sub-01_T1w.pdf
├── input
│ └── sub-01_T1w.nii.gz
└── stats
└── cat_sub-01_T1w.xml
# make project a Datalad dataset
$ datalad create --force .
create(ok): /home/fhoffstaedter/TMP_DATA/OHBM-Edu_research-life-cycle/project (dataset)
# ignore input folder
$ echo "input" > .gitignore
# add all content to dataset
$ datalad save -m "backup project" -d .
add(ok): code (dataset)
add(ok): .gitmodules (file)
add(ok): .gitignore (file)
add(ok): derivatives/ROI_sub-01_T1w.json (file)
add(ok): figures/catreport_sub-01_T1w.pdf (file)
add(ok): stats/cat_sub-01_T1w.xml (file)
save(ok): . (dataset)
action summary:
add (ok: 6)
save (ok: 1)
$ tree project
project
├── code
│ └── do_stuff.sh
├── derivatives
│ └── ROI_sub-01_T1w.json
├── figures
│ └── catreport_sub-01_T1w.pdf
├── input
│ └── sub-01_T1w.nii.gz
└── stats
└── cat_sub-01_T1w.xml
# make project a Datalad dataset
$ datalad create --force .
create(ok): /home/fhoffstaedter/TMP_DATA/OHBM-Edu_research-life-cycle/project (dataset)
# ignore input folder
$ echo "input" > .gitignore
# add all content to dataset
$ datalad save -m "backup project" -d .
add(ok): code (dataset)
add(ok): .gitmodules (file)
add(ok): .gitignore (file)
add(ok): derivatives/ROI_sub-01_T1w.json (file)
add(ok): figures/catreport_sub-01_T1w.pdf (file)
add(ok): stats/cat_sub-01_T1w.xml (file)
save(ok): . (dataset)
action summary:
add (ok: 6)
save (ok: 1)
# create OSF repository
$ datalad create-sibling-osf -s osf \
--title mock_project \
--mode export
create-sibling-osf(ok): https://osf.io/rtgzv/
[INFO ] Configure additional publication dependency on "osf-storage"
configure-sibling(ok): . (sibling)
# push dataset to OSF
$ datalad push --to osf
copy(ok): .datalad/.gitattributes (dataset)
copy(ok): .datalad/config (dataset)
copy(ok): .gitattributes (dataset)
copy(ok): .gitignore (dataset)
copy(ok): .gitmodules (dataset)
copy(ok): derivatives/ROI_sub-01_T1w.json (dataset)
copy(ok): figure/catreport_sub-01_T1w.pdf (dataset)
copy(ok): stats/cat_sub-01_T1w.xml (dataset)
publish(ok): . (dataset) [refs/heads/main->osf:refs/heads/main [new branch]]
publish(ok): . (dataset) [refs/heads/git-annex->osf:refs/heads/git-annex [new branch]]
action summary:
copy (ok: 8)
publish (ok: 2)
# create OSF repository
$ datalad create-sibling-osf -s osf \
--title mock_project \
--mode export
create-sibling-osf(ok): https://osf.io/rtgzv/
[INFO ] Configure additional publication dependency on "osf-storage"
configure-sibling(ok): . (sibling)
# push dataset to OSF
$ datalad push --to osf
copy(ok): .datalad/.gitattributes (dataset)
copy(ok): .datalad/config (dataset)
copy(ok): .gitattributes (dataset)
copy(ok): .gitignore (dataset)
copy(ok): .gitmodules (dataset)
copy(ok): derivatives/ROI_sub-01_T1w.json (dataset)
copy(ok): figure/catreport_sub-01_T1w.pdf (dataset)
copy(ok): stats/cat_sub-01_T1w.xml (dataset)
publish(ok): . (dataset) [refs/heads/main->osf:refs/heads/main [new branch]]
publish(ok): . (dataset) [refs/heads/git-annex->osf:refs/heads/git-annex [new branch]]
action summary:
copy (ok: 8)
publish (ok: 2)
# run fsl robust field of view
$ robustfov -i input/sub-01_T1w.nii.gz -r derivatives/sub-01_fov_T1w.nii.gz
Final FOV is:
0.000000 208.000000 0.000000 240.000000 66.000000 170.000000
# save changes of file
$ datalad save -m "robust field of view" derivatives/sub-01_fov_T1w.nii.gz
# make file editable
$ dl unlock input/sub-01_T1w.nii.gz
unlock(ok): input/sub-01_T1w.nii.gz (file)
# swap left right dimensions of input and save
$ fslswapdim input/sub-01_T1w.nii.gz -x y z input/sub-01_T1w.nii.gz
WARNING:: Flipping Left/Right orientation (as det < 0)
$ datalad save -m "swap wrong left right orientation" input/sub-01_T1w.nii.gz
add(ok): input/sub-01_T1w.nii.gz (file)
save(ok): . (dataset)
action summary:
add (ok: 1)
save (ok: 1)
# run fsl robust field of view
$ robustfov -i input/sub-01_T1w.nii.gz -r derivatives/sub-01_fov_T1w.nii.gz
Final FOV is:
0.000000 208.000000 0.000000 240.000000 66.000000 170.000000
# save changes of file
$ datalad save -m "robust field of view" derivatives/sub-01_fov_T1w.nii.gz
# make file editable
$ dl unlock input/sub-01_T1w.nii.gz
unlock(ok): input/sub-01_T1w.nii.gz (file)
# swap left right dimensions of input
fslswapdim input/sub-01_T1w.nii.gz -x y z input/sub-01_T1w.nii.gz
WARNING:: Flipping Left/Right orientation (as det < 0)
datalad save -m "swap wrong left right orientation" input/sub-01_T1w.nii.gz
add(ok): input/sub-01_T1w.nii.gz (file)
save(ok): . (dataset)
action summary:
add (ok: 1)
save (ok: 1)# go back to original image
$ git reset 5aef1b0320b61080c6b558b4e4cea0ac70f31159 --hard
# swap left right dimensions of input
fslswapdim input/sub-01_T1w.nii.gz -x y z input/sub-01_T1w.nii.gz
WARNING:: Flipping Left/Right orientation (as det < 0)
datalad save -m "swap wrong left right orientation" input/sub-01_T1w.nii.gz
add(ok): input/sub-01_T1w.nii.gz (file)
save(ok): . (dataset)
action summary:
add (ok: 1)
save (ok: 1)
$ robustfov -i input/sub-01_T1w.nii.gz -r derivatives/sub-01_fov_T1w.nii.gz
Final FOV is:
0.000000 208.000000 0.000000 240.000000 66.000000 170.000000
# save changes of file
$ datalad save -m "robust field of view" derivatives/sub-01_fov_T1w.nii.gz
# use datalad run to capture the command
$ datalad run -m "robust field of view" \
robustfov -i input/sub-01_T1w.nii.gz -r derivatives/sub-01_fov_T1w.nii.gz
[INFO ] == Command start (output follows) =====
Final FOV is:
0.000000 208.000000 0.000000 240.000000 66.000000 170.000000
[INFO ] == Command exit (modification check follows) =====
run(ok): /home/fhoffstaedter/TMP_DATA/OHBM-Edu_research-life-cycle/project (dataset) [singularity exec -B /home/fhoffstaedter/...]
add(ok): derivatives/sub-01_fov_T1w.nii.gz (file)
save(ok): . (dataset)
$ git log
commit 468b478d748c7ec8c41be8cf265443fbdd967e37 (HEAD -> main)
Author: Felix Hoffstaedter <f.hoffstaedter@fz-juelich.de>
Date: Fri Jun 5 03:15:28 2026 +0200
[DATALAD RUNCMD] robust field of view
=== Do not change lines below ===
{
"chain": [],
"cmd": "robustfov -i input/sub-01_T1w.nii.gz -r derivatives/sub-01_fov_T1w.nii.gz"
"dsid": "f69b6283-0352-4894-9d44-a1c84fc48484",
"exit": 0,
"extra_inputs": [],
"inputs": [],
"outputs": [],
"pwd": "."
}
^^^ Do not change lines above ^^^