code: def preserve_workspace(
      self, rev: str, name: Optional[str] = None
  ) -> Iterator[Optional[str]]:
      if len(self):
          logger.debug("Clearing existing exp-apply stash")
          self.clear()
      head = self.scm.get_rev()
      self.scm.set_ref(APPLY_HEAD, head)
      message = self.format_message(head, rev, name=name)
      stash_rev = self.push(message=message, include_untracked=True)
      try:
          yield stash_rev
          if stash_rev:
              self._apply_difference(stash_rev, rev)
      except Exception:
          self.revert_workspace()
          raise
  
imports: import re
  from contextlib import contextmanager
  from typing import Dict, Iterable, Iterator, NamedTuple, Optional
  from scmrepo.git import Stash
  from dvc.exceptions import DvcException
  from dvc.log import logger
  from dvc_objects.fs.local import localfs
  from dvc_objects.fs.utils import as_atomic
  from .refs import APPLY_HEAD, APPLY_STASH
label_desc: Use image node_id 3 for calling the ApplyStash obj's underlying member method code with example usage: obj.preserve_workspace(rev, name) without return types