#!/usr/bin/python # -*- coding: utf8 -*- # author : Brian Ernst # python_version : 2.7.6 # ================================= # todo: have a backup feature, make sure files are moved to the recycle bin or a temporary file. # todo: switch to faster method of calling p4 fstat on an entire directory and parsing it's output # todo: add option of using send2trash import inspect, os, re, stat, subprocess, sys, traceback re_remove_comment = re.compile( "#.*$" ) def remove_comment( s ): return re.sub( re_remove_comment, "", s ) try: input = raw_input except: pass def PressEnter( ): print( "\nPress ENTER to continue..." ) s=input( "" ) def main( ): # check requirement if os.system( 'p4 > Nul' ) != 0: print( 'Perforce Command-line Client(p4) is required for this script.' ) sys.exit( 1 ) # Files are added from .p4ignore # Key is the file root, the value is the table of file regexes for that directory. files_to_ignore = {} def get_ignore_list( path ): # have to split path and test top directory dirs = path.split( os.sep ) ignore_list = [ ] for i, val in enumerate( dirs ): path_to_find = os.sep.join( dirs[ : i + 1] ) if path_to_find in files_to_ignore: ignore_list = ignore_list + files_to_ignore[ path_to_find ] return ignore_list def match_in_ignore_list( path, ignore_list ): for r in ignore_list: if re.match( r, path ): return True return False root_path = "." root_full_path = os.getcwd( ) p4_ignore = ".p4ignore" # make sure script doesn't delete itself files_to_ignore[ root_path ] = [ re.compile( os.path.join( re.escape( root_path + os.sep ), os.path.basename( __file__ ) ) ) ] for root, dirs, files in os.walk( root_path ): print ( os.linesep + "Checking '" + root + "' ...") if p4_ignore in files: file_regexes = [] # Should automatically ignore .p4ignore even if it's not specified, otherwise it'll be deleted. path = os.path.join( root, p4_ignore ) with open( path ) as f: for line in f: new_line = remove_comment( line.strip( ) ) if len( new_line ) > 0: file_regexes.append( re.compile( os.path.join( re.escape( root + os.sep ), new_line ) ) ) print( "|Appending ignores from " + path ) files_to_ignore[ root ] = files_to_ignore[ root ] + file_regexes ignore_list = get_ignore_list( root ) #command = "p4 have \"" + root + os.sep + "*\"" command = "p4 fstat *" print("|" + command) os.chdir( root ) proc = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) (out, err) = proc.communicate() os.chdir( root_full_path ) # For ease we're doing a weird solution, rebuilding the file list. This is so we only need to parse unadded files. files = [] for line in err.split( os.linesep ): if len(line) == 0: continue # # dirty hack that grabs the filename from the ends of the printed out (not err) "depo_path - local_path" # # I could use regex to verify the expected string, but that will just slow us down. # basename = os.path.basename( line ) i = line.rfind( ' - ') if i >= 0: basename = line[ : i ] if basename == "*": # Directory is empty, we could delete it now continue path = os.path.join( root, basename ) if not os.path.isdir( path ): files.append( basename ) for file in files: path = os.path.join( root, file ) if match_in_ignore_list( path, ignore_list ): print( "| Ignoring " + path ) continue print( "| " + file + " is unversioned, removing it." ) os.chmod( path, stat.S_IWRITE ) os.remove( path ) dirs_copy = dirs for d in dirs_copy: path = os.path.join( root, d ) if match_in_ignore_list( path, ignore_list ): # add option of using send2trash print( "| Ignoring " + d ) dirs.remove( d ) print( "|Done." ) print( os.linesep + "Removing empty directories...") # remove empty directories for root, dirs, files in os.walk( root_path, topdown=False ): for d in dirs: path = os.path.join( root, d ) if match_in_ignore_list( path, ignore_list ): # add option of using send2trash print( "| ignoring " + d ) dirs.remove( d ) try: os.rmdir(path) print( "| " + d + " was removed." ) except OSError: # Fails on non-empty directory pass print( "|Done." ) if __name__ == "__main__": try: main( ) except: print( "Unexpected error!" ) traceback.print_exc( file = sys.stdout ) PressEnter()