code: def sync(
      state, host, source, destination,
      user=None, group=None, mode=None, delete=False,
      exclude=None, exclude_dir=None, add_deploy_dir=True,
  ):
      '''
      Syncs a local directory with a remote one, with delete support. Note that delete will
      remove extra files on the remote side, but not extra directories.
      + source: local directory to sync
      + destination: remote directory to sync to
      + user: user to own the files and directories
      + group: group to own the files and directories
      + mode: permissions of the files
      + delete: delete remote files not present locally
      + exclude: string or list/tuple of strings to match & exclude files (eg *.pyc)
      + exclude_dir: string or list/tuple of strings to match & exclude directories (eg node_modules)
      '''
      if not source.endswith(path.sep):
          source = '{0}{1}'.format(source, path.sep)
      if add_deploy_dir and state.deploy_dir:
          source = path.join(state.deploy_dir, source)
      if not path.isdir(source):
          raise IOError('No such directory: {0}'.format(source))
      if exclude is not None:
          if not isinstance(exclude, (list, tuple)):
              exclude = [exclude]
      if exclude_dir is not None:
          if not isinstance(exclude_dir, (list, tuple)):
              exclude_dir = [exclude_dir]
      put_files = []
      ensure_dirnames = []
      for dirname, _, filenames in walk(source):
          remote_dirname = dirname.replace(source, '')
          if exclude_dir and any(fnmatch(remote_dirname, match) for match in exclude_dir):
              continue
          if remote_dirname:
              ensure_dirnames.append(remote_dirname)
          for filename in filenames:
              full_filename = path.join(dirname, filename)
              if exclude and any(fnmatch(full_filename, match) for match in exclude):
                  continue
              put_files.append((
                  full_filename,
                  '/'.join(
                      item for item in
                      (destination, remote_dirname, filename)
                      if item
                  ),
              ))
      yield directory(
          state, host, destination,
          user=user, group=group,
      )
      for dirname in ensure_dirnames:
          yield directory(
              state, host,
              '/'.join((destination, dirname)),
              user=user, group=group,
          )
      for local_filename, remote_filename in put_files:
          yield put(
              state, host,
              local_filename, remote_filename,
              user=user, group=group, mode=mode,
              add_deploy_dir=False,
          )
      if delete:
          remote_filenames = set(host.fact.find_files(destination) or [])
          wanted_filenames = set([remote_filename for _, remote_filename in put_files])
          files_to_delete = remote_filenames - wanted_filenames
          for filename in files_to_delete:
              if exclude and any(fnmatch(filename, match) for match in exclude):
                  continue
              yield file(state, host, filename, present=False)