code: def test_save_hierarchy(path=None):
      # this test doesn't use API`remove` to avoid circularities
      ds = make_demo_hierarchy_datasets(path, demo_hierarchy)
      ds.save(recursive=True)
      assert_repo_status(ds.path)
      ds_bb = Dataset(opj(ds.path, "b", "bb"))
      ds_bba = Dataset(opj(ds_bb.path, "bba"))
      ds_bbaa = Dataset(opj(ds_bba.path, "bbaa"))
      # introduce a change at the lowest level
      ds_bbaa.repo.remove("file_bbaa")
      for d in (ds, ds_bb, ds_bba, ds_bbaa):
          ok_(d.repo.dirty)
      # need to give file specifically, otherwise it will simply just preserve
      # staged changes
      ds_bb.save(path=opj(ds_bbaa.path, "file_bbaa"))
      # it has saved all changes in the subtrees spanned
      # by the given datasets, but nothing else
      for d in (ds_bb, ds_bba, ds_bbaa):
          assert_repo_status(d.path)
      ok_(ds.repo.dirty)
      # now with two modified repos
      d = Dataset(opj(ds.path, "d"))
      da = Dataset(opj(d.path, "da"))
      da.repo.remove("file_da")
      db = Dataset(opj(d.path, "db"))
      db.repo.remove("file_db")
      # generator
      d.save(recursive=True)
      for d in (d, da, db):
          assert_repo_status(d.path)
      ok_(ds.repo.dirty)
      # and now with files all over the place and saving
      # all the way to the root
      aa = Dataset(opj(ds.path, "a", "aa"))
      aa.repo.remove("file_aa")
      ba = Dataset(opj(ds.path, "b", "ba"))
      ba.repo.remove("file_ba")
      bb = Dataset(opj(ds.path, "b", "bb"))
      bb.repo.remove("file_bb")
      c = Dataset(opj(ds.path, "c"))
      c.repo.remove("file_c")
      ca = Dataset(opj(ds.path, "c", "ca"))
      ca.repo.remove("file_ca")
      d = Dataset(opj(ds.path, "d"))
      d.repo.remove("file_d")
      ds.save(
          # append trailing slashes to the path to indicate that we want to
          # have the staged content in the dataset saved, rather than only the
          # subdataset state in the respective superds.
          path=[
              opj(p, "")
              for p in (
                  aa.path,
                  ba.path,
                  bb.path,
                  c.path,
                  ca.path,
                  d.path,
              )
          ]
      )
  
imports: import logging
  import re
  from contextlib import contextmanager
  from os.path import exists
  from os.path import join
  from time import sleep
  from datalad.distribution.dataset import Dataset, EnsureDataset, datasetmethod
  from datalad.interface.base import build_doc, eval_results
  from datalad.support.constraints import EnsureKeyChoice, EnsureNone, EnsureStr
  from datalad.support.param import Parameter
  from datalad.tests.utils_pytest import assert_dict_equal, assert_equal, assert_in, assert_not_equal, assert_not_in, assert_raises, assert_re_in, assert_repo_status, assert_result_count, assert_true, ok_, slow, with_tempfile, with_tree
  from datalad.utils import swallow_logs, swallow_outputs
  from ..base import Interface
  from ..results import get_status_dict
  from ..utils import discover_dataset_trace_to_targets, handle_dirty_dataset
label_desc: Use image node_id 5 for calling a global function with example usage: test_save_hierarchy(path) without return types