original_docstring: 
      changelist: Target changelist
      previous_cl: Previous_cl if known (otherwise 0)
      drop: Input file name with a line-by-line file list of filenames
            to exclude from output
      keep: Input file name with a line-by-line file list of filenames
            to limit output to
      force: output all fstat lines even though previous_cl is set. This only affects
             fstat when previous_cl is more recent than changelist (reverse sync).
      add: Create dummy entries from this list,
      verbose: Output stats on stderr after all fstat is listed.
  
      Missing fstat files for changelist and previous_cl are generated automatically.
  
      Streams to stdout the fstat CSV from .o4/<changelist>.fstat.gz
  
      IF not previous_cl:
  
          Stream every entry from changelist (essentially gzcat), while
          applying drop_keep if given.
  
      IF previous_cl == changelist:
  
          Stream nothing.
  
      IF previous_cl < changelist:
  
          Only items in changelist that are newer than previous_cl are
          streamed. Apply drop_keep.
  
      IF previous_cl > changelist
  
          This reverse sync scenario is a little complicated:
  
          * Use the forward sync iterator to determine all files that
            should be synced.
  
          * Find branched or added files and generate false entries to
            have them deleted:
            '<changelist>,0,0,reverse_sync/delete,text,,<path>'
  
          * Add all other file names to KEEP and stream matches at
            <changelist>.
  
      DROP: Exclude fstat from the stream, if the fstat path is in the
            drop-file.
  
      KEEP: Limit fstat from the stream to paths listed in the
            keep-file.
  
      ADD:  Creates dummy entries for the given file to facilitate unsibmitted
            renames between revisions.
      
docstring: Target changelist
  previous_cl: Previous_cl if known (otherwise 0)
  drop: Input file name with a line-by-line file list of filenames
  to exclude from output
  keep: Input file name with a line-by-line file list of filenames
  to limit output to
  force: output all fstat lines even though previous_cl is set. This only affects
  fstat when previous_cl is more recent than changelist (reverse sync).
  add: Create dummy entries from this list,
  verbose: Output stats on stderr after all fstat is listed.
  
  Missing fstat files for changelist and previous_cl are generated automatically.
  
  Streams to stdout the fstat CSV from .o4/.fstat.gz
  
  
  
  Stream every entry from changelist (essentially gzcat), while
  applying drop_keep if given.
  
  IF previous_cl == changelist.
  
  Stream nothing.
  
  IF previous_cl < changelist.
  
  Only items in changelist that are newer than previous_cl are
  streamed.
  
  IF previous_cl > changelist
  
  This reverse sync scenario is a little complicated.
  
  Use the forward sync iterator to determine all files that
  should be synced.
  
  Find branched or added files and generate false entries to
  have them deleted:
  ',0,0,reverse_sync/delete,text,,'
  
  Add all other file names to KEEP and stream matches at
  .
  
  Exclude fstat from the stream, if the fstat path is in the
  drop-file.
  
  Limit fstat from the stream to paths listed in the
  keep-file.
  
  Creates dummy entries for the given file to facilitate unsibmitted
  renames between revisions.
docstring_tokens: Target changelist previous_cl : Previous_cl if known ( otherwise 0 ) drop : Input file name with a line - by - line file list of filenames to exclude from output keep : Input file name with a line - by - line file list of filenames to limit output to force : output all fstat lines even though previous_cl is set . This only affects fstat when previous_cl is more recent than changelist ( reverse sync ) . add : Create dummy entries from this list verbose : Output stats on stderr after all fstat is listed . Missing fstat files for changelist and previous_cl are generated automatically . Streams to stdout the fstat CSV from . o4 / . fstat . gz Stream every entry from changelist ( essentially gzcat ) while applying drop_keep if given . IF previous_cl == changelist . Stream nothing . IF previous_cl < changelist . Only items in changelist that are newer than previous_cl are streamed . IF previous_cl > changelist This reverse sync scenario is a little complicated . Use the forward sync iterator to determine all files that should be synced . Find branched or added files and generate false entries to have them deleted : ' 0 0 reverse_sync / delete text ' Add all other file names to KEEP and stream matches at . Exclude fstat from the stream if the fstat path is in the drop - file . Limit fstat from the stream to paths listed in the keep - file . Creates dummy entries for the given file to facilitate unsibmitted renames between revisions .
code: def o4_fstat(changelist,
               previous_cl,
               drop=None,
               keep=None,
               quiet=False,
               force=False,
               add=None,
               verbose=False):
      """
      changelist: Target changelist
      previous_cl: Previous_cl if known (otherwise 0)
      drop: Input file name with a line-by-line file list of filenames
            to exclude from output
      keep: Input file name with a line-by-line file list of filenames
            to limit output to
      force: output all fstat lines even though previous_cl is set. This only affects
             fstat when previous_cl is more recent than changelist (reverse sync).
      add: Create dummy entries from this list,
      verbose: Output stats on stderr after all fstat is listed.
  
      Missing fstat files for changelist and previous_cl are generated automatically.
  
      Streams to stdout the fstat CSV from .o4/<changelist>.fstat.gz
  
      IF not previous_cl:
  
          Stream every entry from changelist (essentially gzcat), while
          applying drop_keep if given.
  
      IF previous_cl == changelist:
  
          Stream nothing.
  
      IF previous_cl < changelist:
  
          Only items in changelist that are newer than previous_cl are
          streamed. Apply drop_keep.
  
      IF previous_cl > changelist
  
          This reverse sync scenario is a little complicated:
  
          * Use the forward sync iterator to determine all files that
            should be synced.
  
          * Find branched or added files and generate false entries to
            have them deleted:
            '<changelist>,0,0,reverse_sync/delete,text,,<path>'
  
          * Add all other file names to KEEP and stream matches at
            <changelist>.
  
      DROP: Exclude fstat from the stream, if the fstat path is in the
            drop-file.
  
      KEEP: Limit fstat from the stream to paths listed in the
            keep-file.
  
      ADD:  Creates dummy entries for the given file to facilitate unsibmitted
            renames between revisions.
      """
  
      if os.environ.get('DEBUG', ''):
          print(f"""# o4 fstat {os.getcwd()}
  # changelist: {changelist}
  # previous_cl: {previous_cl}
  # drop: {drop}
  # keep: {keep}
  # quiet: {quiet}""",
                file=sys.stderr)
      o4_log('fstat',
             _depot_path(),
             changelist=changelist,
             previous_cl=previous_cl,
             drop=drop,
             keep=keep,
             quiet=quiet,
             force=force,
             add=add,
             verbose=verbose)
  
      if previous_cl:
          previous_cl = int(previous_cl)
          if previous_cl == changelist:
              return changelist
      else:
          previous_cl = 0
  
      if quiet:
          if drop or keep:
              sys.exit("*** ERROR: Quiet fstat does not support drop or keep.")
          actual_cl = max(
              int(f.split(',', 1)[0]) for f in fstat_iter(_depot_path(), changelist, previous_cl))
          print(f'*** INFO: Created {os.getcwd()}/.o4/{actual_cl}.fstat.gz')
          return actual_cl
  
      if drop:
          with open(drop, 'rt', encoding='utf8') as fin:
              drop = set(f[:-1] for f in fin)
      if keep:
          with open(keep, 'rt', encoding='utf8') as fin:
              keep = set(f[:-1] for f in fin)
      for fname in add:
          fname = fname.replace(';', ';;').replace(',', ';.')
          print(f"{changelist},{fname},USECL,0,DEADFACEDEADFACEDEADFACEDEADFACE")
  
      if previous_cl and previous_cl > changelist:
          # Syncing backwards requires us to delete files that were added
          # between the lower and higher changelist. All other files must
          # be synced to their state at the lower changelist.
          past_filenames = set(p for p, _ in map(
              fstat_path,
              progress_iter(fstat_iter(_depot_path(), changelist),
                            os.getcwd() + '/.o4/.fstat', 'fstat-reverse')) if p)
          if not keep:
              keep = set()
          if not drop:
              drop = set()
          for f in map(
                  fstat_split,
                  progress_iter(fstat_iter(_depot_path(), previous_cl, changelist),
                                os.getcwd() + '/.o4/.fstat', 'fstat-reverse')):
              if not f:
                  continue
              if f[F_PATH] not in past_filenames:
                  fpath = f[F_PATH].replace(';', ';;').replace(',', ';.')
                  print(f'{changelist},{fpath},0,0,')
                  if force:
                      drop.add(f[F_PATH])
              elif not force:
                  keep.add(f[F_PATH])
          previous_cl = 0
  
      if drop and keep:
          # Prioritize dropping over keeping, if a file is in both.
          # Any file that is currently opened by Perforce must be dropped.
          # This function assumes that a supplied drop list is a list of
          # open files (which then gets augmented (above) with files that
          # did not exist at the lower changelist).
          keep = keep.difference(drop)
      drop_n = 0 if not drop else len(drop)
      keep_n = 0 if not keep else len(keep)
      if not drop:
          drop = None
      if not keep:
          keep = None
  
      fstats = progress_iter(fstat_iter(_depot_path(), changelist, previous_cl),
                             os.getcwd() + '/.o4/.fstat', 'fstat')
      # Can't break out of fstat_iter without risking that the local
      # cache is not created, causing fstat_from_perforce to be called
      # twice, so we use an iterator that we can drain.
      n_fstats = 0
      n_deleted = 0
      for line in fstats:
          if verbose:
              n_fstats += 1
              if line.endswith(','):
                  n_deleted += 1
          if keep is not None or drop is not None:
              path, line = fstat_path(line)
              if not path:
                  continue
              if drop is not None:
                  drop.discard(path)
                  if len(drop) != drop_n:
                      drop_n -= 1
                      if not drop_n:
                          drop = None
                      continue
              if keep is not None:
                  keep.discard(path)
                  if len(keep) == keep_n:
                      continue
                  keep_n -= 1
                  if not keep_n:
                      print(line)
                      # Make sure the iterator is consumed, so that
                      # local cache is created.
                      sum(0 for line in fstats)
                      break
          print(line)
      actual_cl, fname = get_fstat_cache(changelist)
      if verbose:
          print({'files': n_fstats, 'deleted': n_deleted}, file=sys.stderr)
      return actual_cl
code_tokens: def o4_fstat ( changelist , previous_cl , drop = None , keep = None , quiet = False , force = False , add = None , verbose = False ) : if os . environ . get ( 'DEBUG' , '' ) : print ( f"""# o4 fstat {os.getcwd()}
  # changelist: {changelist}
  # previous_cl: {previous_cl}
  # drop: {drop}
  # keep: {keep}
  # quiet: {quiet}""" , file = sys . stderr ) o4_log ( 'fstat' , _depot_path ( ) , changelist = changelist , previous_cl = previous_cl , drop = drop , keep = keep , quiet = quiet , force = force , add = add , verbose = verbose ) if previous_cl : previous_cl = int ( previous_cl ) if previous_cl == changelist : return changelist else : previous_cl = 0 if quiet : if drop or keep : sys . exit ( "*** ERROR: Quiet fstat does not support drop or keep." ) actual_cl = max ( int ( f . split ( ',' , 1 ) [ 0 ] ) for f in fstat_iter ( _depot_path ( ) , changelist , previous_cl ) ) print ( f'*** INFO: Created {os.getcwd()}/.o4/{actual_cl}.fstat.gz' ) return actual_cl if drop : with open ( drop , 'rt' , encoding = 'utf8' ) as fin : drop = set ( f [ : - 1 ] for f in fin ) if keep : with open ( keep , 'rt' , encoding = 'utf8' ) as fin : keep = set ( f [ : - 1 ] for f in fin ) for fname in add : fname = fname . replace ( ';' , ';;' ) . replace ( ',' , ';.' ) print ( f"{changelist},{fname},USECL,0,DEADFACEDEADFACEDEADFACEDEADFACE" ) if previous_cl and previous_cl > changelist : past_filenames = set ( p for p , _ in map ( fstat_path , progress_iter ( fstat_iter ( _depot_path ( ) , changelist ) , os . getcwd ( ) + '/.o4/.fstat' , 'fstat-reverse' ) ) if p ) if not keep : keep = set ( ) if not drop : drop = set ( ) for f in map ( fstat_split , progress_iter ( fstat_iter ( _depot_path ( ) , previous_cl , changelist ) , os . getcwd ( ) + '/.o4/.fstat' , 'fstat-reverse' ) ) : if not f : continue if f [ F_PATH ] not in past_filenames : fpath = f [ F_PATH ] . replace ( ';' , ';;' ) . replace ( ',' , ';.' ) print ( f'{changelist},{fpath},0,0,' ) if force : drop . add ( f [ F_PATH ] ) elif not force : keep . add ( f [ F_PATH ] ) previous_cl = 0 if drop and keep : keep = keep . difference ( drop ) drop_n = 0 if not drop else len ( drop ) keep_n = 0 if not keep else len ( keep ) if not drop : drop = None if not keep : keep = None fstats = progress_iter ( fstat_iter ( _depot_path ( ) , changelist , previous_cl ) , os . getcwd ( ) + '/.o4/.fstat' , 'fstat' ) n_fstats = 0 n_deleted = 0 for line in fstats : if verbose : n_fstats += 1 if line . endswith ( ',' ) : n_deleted += 1 if keep is not None or drop is not None : path , line = fstat_path ( line ) if not path : continue if drop is not None : drop . discard ( path ) if len ( drop ) != drop_n : drop_n -= 1 if not drop_n : drop = None continue if keep is not None : keep . discard ( path ) if len ( keep ) == keep_n : continue keep_n -= 1 if not keep_n : print ( line ) sum ( 0 for line in fstats ) break print ( line ) actual_cl , fname = get_fstat_cache ( changelist ) if verbose : print ( { 'files' : n_fstats , 'deleted' : n_deleted } , file = sys . stderr ) return actual_cl
short_docstring: changelist: Target changelist
  previous_cl: Previous_cl if known (otherwise 0)
  drop: Input file name with a line-by-line file list of filenames
  to exclude from output
  keep: Input file name with a line-by-line file list of filenames
  to limit output to
  force: output all fstat lines even though previous_cl is set.
short_docstring_tokens: changelist : Target changelist previous_cl : Previous_cl if known ( otherwise 0 ) drop : Input file name with a line - by - line file list of filenames to exclude from output keep : Input file name with a line - by - line file list of filenames to limit output to force : output all fstat lines even though previous_cl is set .
comment: """
      changelist: Target changelist
      previous_cl: Previous_cl if known (otherwise 0)
      drop: Input file name with a line-by-line file list of filenames
            to exclude from output
      keep: Input file name with a line-by-line file list of filenames
            to limit output to
      force: output all fstat lines even though previous_cl is set. This only affects
             fstat when previous_cl is more recent than changelist (reverse sync).
      add: Create dummy entries from this list,
      verbose: Output stats on stderr after all fstat is listed.
  
      Missing fstat files for changelist and previous_cl are generated automatically.
  
      Streams to stdout the fstat CSV from .o4/<changelist>.fstat.gz
  
      IF not previous_cl:
  
          Stream every entry from changelist (essentially gzcat), while
          applying drop_keep if given.
  
      IF previous_cl == changelist:
  
          Stream nothing.
  
      IF previous_cl < changelist:
  
          Only items in changelist that are newer than previous_cl are
          streamed. Apply drop_keep.
  
      IF previous_cl > changelist
  
          This reverse sync scenario is a little complicated:
  
          * Use the forward sync iterator to determine all files that
            should be synced.
  
          * Find branched or added files and generate false entries to
            have them deleted:
            '<changelist>,0,0,reverse_sync/delete,text,,<path>'
  
          * Add all other file names to KEEP and stream matches at
            <changelist>.
  
      DROP: Exclude fstat from the stream, if the fstat path is in the
            drop-file.
  
      KEEP: Limit fstat from the stream to paths listed in the
            keep-file.
  
      ADD:  Creates dummy entries for the given file to facilitate unsibmitted
            renames between revisions.
      """$SEP$# Syncing backwards requires us to delete files that were added$SEP$# between the lower and higher changelist. All other files must$SEP$# be synced to their state at the lower changelist.$SEP$# Prioritize dropping over keeping, if a file is in both.$SEP$# Any file that is currently opened by Perforce must be dropped.$SEP$# This function assumes that a supplied drop list is a list of$SEP$# open files (which then gets augmented (above) with files that$SEP$# did not exist at the lower changelist).$SEP$# Can't break out of fstat_iter without risking that the local$SEP$# cache is not created, causing fstat_from_perforce to be called$SEP$# twice, so we use an iterator that we can drain.$SEP$# Make sure the iterator is consumed, so that$SEP$# local cache is created.