from __future__ import print_function import P4 import P4Triggers import sys import os class DirEntry: def __init__(self, parent, fullPath): self.parent = parent self.fullPath = fullPath def makePath(self, name): return os.path.join(self.fullPath, name) def __repr__(self): return self.fullPath class FileDirectoryProtector( P4Triggers.P4Trigger ): """FileDirectoryProtector is a subclass of P4Trigger. Use this trigger to ensure that your depot does not contain files and directories with exactly the same name. If you have a filename that matches a directory name with the same path, your depot is in trouble. Syncs will fail with funny error messages or subsequently toggle between file and directory - and only an obliterate can resolve this issue. This trigger is designed to prevent you to get into this illegal state in the first place. """ def __init__( self, **kargs): kargs['api_level'] = 82 P4Triggers.P4Trigger.__init__(self, **kargs) # need to reset the args in case a p4config file overwrote them for (k,v) in kargs.items(): if k != "log": setattr( self.p4, k, v) self.depots = {} self.directories = {} self.fileEntries = [] def setUp( self ): info = self.p4.run_info()[0] if "unicode" in info and info["unicode"] == "enabled": self.p4.charset = "utf8" self.p4.exception_level = 1 # ignore WARNINGS like "no such file" self.p4.prog = "CheckCaseTrigger" self.caseSensitive = (info["caseHandling"] == "sensitive") self.USER_MESSAGE=""" Your submission has been rejected because the following files or directories already exist as a directory or file. """ self.BADFILE_FORMAT=""" Your file: '%s' existing file/dir: '%s' """ def validate( self ): """Here the fun begins. This method overrides P4Trigger.validate()""" badlist = {} files = self.change.files self.build_list(files) return len(badlist) == 0 # Idea: # Break the files in the change down into directories + file # Build a tree hierarchy of directories # Then: # Check directories against existing files # Check files against existing directories def build_list(self, files): for file in files: df = file.depotFile action = file.revisions[0].action if not (action == "add" or action == "branch"): continue # a bit of hokus pokus. We want to split the path, include the depot but get rid of the first two slashes split_list = df[2:].split('/') depot = split_list[0] dirs = split_list[1:-1] filename = split_list[-1] if depot in self.depots: root = self.depots[depot] else: root = DirEntry(None, depot) self.depots[depot] = root for d in dirs: path = root.makePath(d) if path in self.directories: root = self.directories[path] else: root = DirEntry(root, path) self.directories[path] = root self.fileEntries.append(DirEntry(root, root.makePath(filename))) print("Depots:") for d in self.depots: print(d) print("\nDirectories:") for d in self.directories: print(d) print("\nFiles:") for f in self.fileEntries: print(f) # main routine. # If called from the command line, go in here if __name__ == "__main__": kargs = {} try: for arg in sys.argv[2:]: (key,value) = arg.split("=") kargs[key] = value except Exception as e : print("Error, expecting arguments in form key=value. Bailing out ...") print(e) sys.exit(0) ct = FileDirectoryProtector(**kargs) sys.exit( ct.parseChange( sys.argv[1] ) )