def do_genpatch(self, argv): """ genpatch -- generate a patch from a pending or submitted changelist px genpatch [<changelist#>] Generate a patch (i.e. can later be used as input for the 'patch' program) for a given changelist number. If no change number is given then a patch for the 'default' changelist is generated. The patch is printed on stdout. Files opened for 'add', 'delete' or 'branch' are inlined such that application with 'patch' will create or delete the intended files. """ #TODO: # - Would an optional [<files> ...] argument be useful or is # that overkill? E.g. 'p4 genpatch ./...' (I think that that # would be very useful. # - Could add '-f' option to only warn on 'out of sync'. # - Could add '-d<flag>' option to control to diff format. # Context and unified allowed. # - Handling binary files that cannot be diff'd # - Option to be able to control the base dir so the patch -p# # number can be controlled. Dunno what form that should # take. # Process options. diffFormat = 'u' if diffFormat == 'u': prefixes = ('---', '+++') elif diffFormat == 'c': prefixes = ('***', '---') # Process args. if not argv[1:]: change = 'default' elif len(argv[1:]) == 1: change = argv[1] try: change = int(change) except ValueError: # Stupidly, p4win's new Tool %c interpolation will use # "Default", on which the normal p4.exe client will die. change = change.lower() if change != 'default': sys.stderr.write("Invalid changelist number '%s'.\n"\ % change) return 1 else: sys.stderr.write("Usage: genpatch [<changelist#>]\n") sys.stderr.write("Missing/wrong number of arguments.\n") return 1 # Validate the given change number. p4 = p4lib.P4( **p4lib.parseOptv(self.__p4optv) ) submitted = [c['change'] for c in p4.changes(status='submitted')] pending = [c['change'] for c in p4.changes(status='pending')] if change in submitted: status = 'submitted' elif change in pending+['default']: status = 'pending' else: sys.stderr.write("Change %s unknown." % change) return 1 # Get list of files to include in patch. if status == 'submitted': d = p4.describe(change, diffFormat='u') desc = d['description'] files = d['files'] diffs = d['diff'] elif status == 'pending': files = p4.opened(change=change) if change == 'default': desc = None else: desc = p4.change(change=change)['description'] if files: diffs = p4.diff([f['depotFile'] for f in files], diffFormat='u') else: diffs = [] # Make a single string from 'diffs' with appropriate delimiters # for the "patch" program. diffstr = '' timestamp = time.asctime() for diff in diffs: # Perforce std header, e.g.: # ==== //depot/apps/px/ReadMe.txt#5 (text) ==== # or # ==== //depot/foo.doc#42 - c:\trentm\foo.doc ==== (binary) if diff.has_key('localFile'): diffstr += "==== %(depotFile)s#%(rev)s - %(localFile)s ===="\ % diff if diff['binary']: diffstr += " (binary)" diffstr += "\n" else: diffstr += "==== %(depotFile)s#%(rev)s (%(type)s) ====\n"\ % diff # Patch header, e.g. for unified diffs: # Index: apps/px/test/ToDo.txt # --- apps/px/test/ToDo.txt.~1~ Fri May 31 21:17:17 2002 # +++ apps/px/test/ToDo.txt Fri May 31 21:17:17 2002 # or for context diffs: # Index: apps/px/test/ToDo.txt # *** apps/px/test/ToDo.txt.~1~ Fri May 31 21:26:47 2002 # --- apps/px/test/ToDo.txt Fri May 31 21:26:47 2002 fname = diff['depotFile'][len('//depot/'):] if diff.has_key('text'): diffstr += "Index: %s\n" % fname diffstr += "%s %s.~1~\t%s\n" % (prefixes[0], fname, timestamp) diffstr += "%s %s\t%s\n" % (prefixes[1], fname, timestamp) # The diff text. diffstr += ''.join(diff['text']) if diffstr[-1] != '\n': diffstr += "\n\\ No newline at end of file\n" # Inline added files into the diff. addedfiles = [f for f in files if f['action'] in ('add', 'branch')] for f in addedfiles: # May have to get file type from 'p4 files'. if status == 'submitted': f['type'] = p4.files(f['depotFile'])[0]['type'] # Skip file if it is binary. if f['type'].startswith('binary'): log.warn("Cannot inline '%s' because it is binary."\ % f['depotFile']) continue # Get the file contents. if status == "pending": # Read the file contents from disk. localFile = p4.where(f['depotFile'])[0]['localFile'] if not os.path.exists(localFile): continue lines = open(localFile, 'r').readlines() else: # Get the file contents via 'p4 print'. fnameRev = "%s#%s" % (f['depotFile'], f['rev']) lines = p4.print_(fnameRev)[0]['text'].split('\n') if not lines[-1]: lines = lines[:-1] # drop empty last line lines = [line+'\n' for line in lines] # Inline the file. diffstr += "\n==== %(depotFile)s#%(rev)s (%(type)s) ====\n" % f if len(lines) < 2: ln = "" else: ln = "," + str(len(lines)) fname = f['depotFile'][len('//depot/'):] diffstr += "Index: %s\n" % fname diffstr += "%s %s.~1~\t%s\n" % (prefixes[0], fname, timestamp) diffstr += "%s %s\t%s\n" % (prefixes[1], fname, timestamp) diffstr += "@@ -0,0 +1%s @@\n" % ln diffstr += '+' + '+'.join(lines) if diffstr[-1] != '\n': diffstr += "\n\\ No newline at end of file\n" if diffstr: # std patch terminator diffstr += "End of Patch." patch = p4lib.makeForm(description=desc, files=files, differences=diffstr) if patch: # ViM-specific hack to have it colorize patches as diffs. patch = "diff\n" + patch sys.stdout.write(patch)
describe = 0 for opt, optarg in optlist: if opt == '-i': followIntegrations = 1 elif opt == '-l': longOutput = 1 elif opt == '-m': max = int(optarg) elif opt == '-s': status = optarg elif opt == '-d': describe = 1 if describe: # Get a list of change numbers to describe. p4 = p4lib.P4( **p4lib.parseOptv(self.__p4optv) ) changes = p4.changes(args, followIntegrations=followIntegrations, longOutput=longOutput, max=max, status=status) changeNums = [c['change'] for c in changes] log.info("Changenums to describe: %s" % changeNums) # Describe each change. for num in changeNums: retval = self._p4run(['describe', '-du', str(num)]) if retval: raise PxError("Error running '%s': retval=%s"\ % (cmd, retval)) else: return self._p4run(argv)