class_docstr: Update a dataset from a sibling.
  
      
class_docstr_tok: ["Update","a","dataset","from","a","sibling","."]
code: def __call__(
      path=None,
      *,
      sibling=None,
      merge=False,
      how=None,
      how_subds=None,
      follow="sibling",
      dataset=None,
      recursive=False,
      recursion_limit=None,
      fetch_all=None,
      reobtain_data=False,
  ):
      if fetch_all is not None:
          lgr.warning(
              "update(fetch_all=...) called. Option has no effect, and will be removed"
          )
      if path and not recursive:
          lgr.warning(
              "path constraints for subdataset updates ignored, "
              "because `recursive` option was not given"
          )
  
      how, how_subds = _process_how_args(merge, how, how_subds)
      # `merge` should be considered through `how` and `how_subds` only.
      # Unbind `merge` to ensure that downstream code doesn't look at it.
      del merge
  
      refds = require_dataset(
          dataset, check_installed=True, purpose="update"
      )
  
      save_paths = []
      update_failures = set()
      saw_subds = False
      for ds, revision in itertools.chain(
          [(refds, None)],
          refds.subdatasets(
              path=path,
              state="present",
              recursive=recursive,
              recursion_limit=recursion_limit,
              return_type="generator",
              result_renderer="disabled",
              result_xfm=YieldDatasetAndRevision(),
          )
          if recursive
          else [],
      ):
          if ds != refds:
              saw_subds = True
          repo = ds.repo
          is_annex = isinstance(repo, AnnexRepo)
          # prepare return value
          res = get_status_dict(
              "update", ds=ds, logger=lgr, refds=refds.path
          )
  
          follow_parent = revision and follow.startswith("parentds")
          follow_parent_lazy = revision and follow == "parentds-lazy"
          if (
              follow_parent_lazy
              and repo.get_hexsha(repo.get_corresponding_branch())
              == revision
          ):
              res["message"] = (
                  "Dataset already at commit registered in parent: %s",
                  repo.path,
              )
              res["status"] = "notneeded"
              yield res
              continue
  
          how_curr = how_subds if revision else how
          # get all remotes which have references (would exclude
          # special remotes)
          remotes = repo.get_remotes(
              **({"exclude_special_remotes": True} if is_annex else {})
          )
          if not remotes and not sibling:
              res["message"] = (
                  "No siblings known to dataset at %s\nSkipping",
                  repo.path,
              )
              res["status"] = "notneeded"
              yield res
              continue
          curr_branch = repo.get_active_branch()
          tracking_remote = None
          if not sibling and len(remotes) == 1:
              # there is only one remote, must be this one
              sibling_ = remotes[0]
          elif not sibling:
              # nothing given, look for tracking branch
              tracking_remote = repo.get_tracking_branch(
                  branch=curr_branch, remote_only=True
              )[0]
              sibling_ = tracking_remote
          else:
              sibling_ = sibling
          if sibling_ and sibling_ not in remotes:
              res["message"] = (
                  "'%s' not known to dataset %s\nSkipping",
                  sibling_,
                  repo.path,
              )
              res["status"] = "impossible"
              yield res
              continue
          if not sibling_ and len(remotes) > 1 and how_curr:
              lgr.debug("Found multiple siblings:\n%s", remotes)
              res["status"] = "impossible"
              res[
                  "message"
              ] = "Multiple siblings, please specify from which to update."
              yield res
              continue
          lgr.info("Fetching updates for %s", ds)
          # fetch remote
          fetch_kwargs = dict(
              # test against user-provided value!
              remote=None if sibling is None else sibling_,
              all_=sibling is None,
              git_options=[
                  # required to not trip over submodules that were removed in
                  # the origin clone
                  "--no-recurse-submodules",
                  # prune to not accumulate a mess over time
                  "--prune",
              ],
          )
          if not (follow_parent_lazy and repo.commit_exists(revision)):
              try:
                  repo.fetch(**fetch_kwargs)
              except CommandError as exc:
                  ce = CapturedException(exc)
                  yield get_status_dict(
                      status="error",
                      message=("Fetch failed: %s", ce),
                      exception=ce,
                      **res,
                  )
                  continue
  
          # NOTE reevaluate ds.repo again, as it might have be converted from
          # a GitRepo to an AnnexRepo
          repo = ds.repo
  
          if follow_parent and not repo.commit_exists(revision):
              if sibling_:
                  try:
                      lgr.debug(
                          "Fetching revision %s directly for %s",
                          revision,
                          repo,
                      )
                      repo.fetch(
                          remote=sibling_,
                          refspec=revision,
                          git_options=["--recurse-submodules=no"],
                      )
                  except CommandError as exc:
                      ce = CapturedException(exc)
                      yield dict(
                          res,
                          status="impossible",
                          message=(
                              "Attempt to fetch %s from %s failed: %s",
                              revision,
                              sibling_,
                              ce,
                          ),
                          exception=ce,
                      )
                      continue
              else:
                  yield dict(
                      res,
                      status="impossible",
                      message=(
                          "Need to fetch %s directly "
                          "but single sibling not resolved",
                          revision,
                      ),
                  )
                  continue
  
          saw_update_failure = False
          if how_curr:
              if follow_parent:
                  target = revision
              else:
                  target = _choose_update_target(
                      repo, curr_branch, sibling_, tracking_remote
                  )
  
              adjusted = is_annex and repo.is_managed_branch(
                  curr_branch
              )
              if adjusted:
                  if follow_parent:
                      yield dict(
                          res,
                          status="impossible",
                          message=(
                              "follow='parentds' is incompatible "
                              "with adjusted branches"
                          ),
                      )
                      continue
                  if how_curr != "merge":
                      yield dict(
                          res,
                          status="impossible",
                          message=(
                              "Updating via '%s' is incompatible "
                              "with adjusted branches",
                              how_curr,
                          ),
                      )
                      continue
  
              update_fn = _choose_update_fn(
                  repo, how_curr, is_annex=is_annex, adjusted=adjusted
              )
  
              fn_opts = ["--ff-only"] if how_curr == "ff-only" else None
              if update_fn is not _annex_sync:
                  if target is None:
                      yield dict(
                          res,
                          status="impossible",
                          message="Could not determine update target",
                      )
                      continue
  
              if is_annex and reobtain_data:
                  update_fn = _reobtain(ds, update_fn)
  
              for ures in update_fn(
                  repo, sibling_, target, opts=fn_opts
              ):
                  # NOTE: Ideally the "merge" action would also be prefixed
                  # with "update.", but a plain "merge" is used for backward
                  # compatibility.
                  if ures["status"] != "ok" and (
                      ures["action"] == "merge"
                      or ures["action"].startswith("update.")
                  ):
                      saw_update_failure = True
                  yield dict(res, **ures)
  
          if saw_update_failure:
              update_failures.add(ds)
              res["status"] = "error"
              res["message"] = ("Update of %s failed", target)
          else:
              res["status"] = "ok"
              save_paths.append(ds.path)
          yield res
      # we need to save updated states only if merge was requested -- otherwise
      # it was a pure fetch
      if how_curr and recursive:
          yield from _save_after_update(
              refds, save_paths, update_failures, path, saw_subds
          )
  
imports: import logging
  from os.path import lexists
  import itertools
  from datalad.interface.base import Interface
  from datalad.interface.base import build_doc, eval_results
  from datalad.interface.results import get_status_dict, YieldDatasets
  from datalad.support.constraints import EnsureBool, EnsureChoice, EnsureStr, EnsureNone
  from datalad.support.annexrepo import AnnexRepo
  from datalad.support.exceptions import CapturedException, CommandError
  from datalad.support.param import Parameter
  from datalad.interface.common_opts import recursion_flag, recursion_limit
  from datalad.distribution.dataset import require_dataset
  from .dataset import EnsureDataset, datasetmethod
label_desc: Use image node_id 1 for calling the Update obj's underlying member method code with example usage: obj.__call__(path) without return types