From d155415f301deda2f44e1be241d73c9925d8465f Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 14 Jul 2026 16:30:28 -0500 Subject: [PATCH] Write a BIDS-friendly .gitattributes at babs init babs init gave the analysis dataset yoda's default .gitattributes, which annexes everything except .git*/README/CHANGELOG, so BIDS metadata sidecars (dataset_description.json, small *.json/*.tsv) were annexed. Overwrite it with a rule that keeps small text in git and everything else in the annex: a file is annexed if it is binary (mimeencoding) or larger than 40kb, with explicit overrides forcing listed files into git. Fixes #378 Co-Authored-By: Claude Opus 4.8 (1M context) --- babs/bootstrap.py | 24 ++++++++++++++++++++++++ tests/test_base.py | 8 +++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/babs/bootstrap.py b/babs/bootstrap.py index 9ea25a4f..70d6f7ed 100644 --- a/babs/bootstrap.py +++ b/babs/bootstrap.py @@ -21,6 +21,21 @@ validate_processing_level, ) +# Analysis-dataset `.gitattributes`: annex a file if it is binary +# (`mimeencoding=binary`, which needs git-annex's MagicMime build flag) or +# larger than 40kb; the explicit lines force listed files into git regardless. +BIDS_GITATTRIBUTES = """\ +* annex.backend=MD5E +* annex.largefiles=((mimeencoding=binary)or(largerthan=40kb)) +**/.git* annex.largefiles=nothing +.babs/** annex.largefiles=nothing +dataset_description.json annex.largefiles=nothing +.bidsignore annex.largefiles=nothing +README* annex.largefiles=nothing +CHANGES annex.largefiles=nothing +LICENSE annex.largefiles=nothing +""" + class BABSBootstrap(BABS): """A BABS subclass that implements the bootstrap process.""" @@ -121,6 +136,15 @@ def babs_bootstrap( if self.shared_group is not None: create_kwargs['initopts'] = ['--shared=group'] self._analysis_datalad_handle = dlapi.create(self.analysis_path, **create_kwargs) + + # Overwrite the create-time `.gitattributes` before any content is + # saved, so the annex policy applies to every file (see + # BIDS_GITATTRIBUTES): + gitattributes_path = op.join(self.analysis_path, '.gitattributes') + with open(gitattributes_path, 'w') as f: + f.write(BIDS_GITATTRIBUTES) + self.datalad_save(path='.gitattributes', message='Use a BIDS-friendly .gitattributes') + self.input_datasets.update_abs_paths(Path(self.analysis_path)) self.input_datasets.set_inclusion_dataframe(initial_inclusion_df, processing_level) diff --git a/tests/test_base.py b/tests/test_base.py index 76517ff1..aeca4830 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -17,10 +17,16 @@ from babs import BABSCheckSetup from babs.base import BABS, CONFIG_SECTIONS -from babs.bootstrap import BABSBootstrap +from babs.bootstrap import BIDS_GITATTRIBUTES, BABSBootstrap from babs.utils import read_yaml +def test_bootstrap_writes_bids_gitattributes(babs_project_sessionlevel): + """`babs init` installs the BIDS-friendly `.gitattributes` in `analysis/`.""" + gitattributes = Path(babs_project_sessionlevel) / 'analysis' / '.gitattributes' + assert gitattributes.read_text() == BIDS_GITATTRIBUTES + + def test_missing_config_parts(babs_project_sessionlevel): """Test that missing config parts raise an error."""