diff options
Diffstat (limited to 'git-p4.py')
-rwxr-xr-x | git-p4.py | 175 |
1 files changed, 119 insertions, 56 deletions
@@ -160,17 +160,42 @@ def p4_write_pipe(c, stdin): real_cmd = p4_build_cmd(c) return write_pipe(real_cmd, stdin) -def read_pipe(c, ignore_error=False): +def read_pipe_full(c): + """ Read output from command. Returns a tuple + of the return status, stdout text and stderr + text. + """ if verbose: sys.stderr.write('Reading pipe: %s\n' % str(c)) expand = isinstance(c,basestring) p = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=expand) (out, err) = p.communicate() - if p.returncode != 0 and not ignore_error: - die('Command failed: %s\nError: %s' % (str(c), err)) + return (p.returncode, out, err) + +def read_pipe(c, ignore_error=False): + """ Read output from command. Returns the output text on + success. On failure, terminates execution, unless + ignore_error is True, when it returns an empty string. + """ + (retcode, out, err) = read_pipe_full(c) + if retcode != 0: + if ignore_error: + out = "" + else: + die('Command failed: %s\nError: %s' % (str(c), err)) return out +def read_pipe_text(c): + """ Read output from a command with trailing whitespace stripped. + On error, returns None. + """ + (retcode, out, err) = read_pipe_full(c) + if retcode != 0: + return None + else: + return out.rstrip() + def p4_read_pipe(c, ignore_error=False): real_cmd = p4_build_cmd(c) return read_pipe(real_cmd, ignore_error) @@ -288,7 +313,7 @@ def p4_move(src, dest): p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)]) def p4_last_change(): - results = p4CmdList(["changes", "-m", "1"]) + results = p4CmdList(["changes", "-m", "1"], skip_info=True) return int(results[0]['change']) def p4_describe(change): @@ -296,7 +321,7 @@ def p4_describe(change): the presence of field "time". Return a dict of the results.""" - ds = p4CmdList(["describe", "-s", str(change)]) + ds = p4CmdList(["describe", "-s", str(change)], skip_info=True) if len(ds) != 1: die("p4 describe -s %d did not return 1 result: %s" % (change, str(ds))) @@ -484,7 +509,7 @@ def isModeExec(mode): def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) -def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None): +def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False): if isinstance(cmd,basestring): cmd = "-G " + cmd @@ -520,6 +545,9 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None): try: while True: entry = marshal.load(p4.stdout) + if skip_info: + if 'code' in entry and entry['code'] == 'info': + continue if cb is not None: cb(entry) else: @@ -577,12 +605,7 @@ def p4Where(depotPath): return clientPath def currentGitBranch(): - retcode = system(["git", "symbolic-ref", "-q", "HEAD"], ignore_error=True) - if retcode != 0: - # on a detached head - return None - else: - return read_pipe(["git", "name-rev", "HEAD"]).split(" ")[1].strip() + return read_pipe_text(["git", "symbolic-ref", "--short", "-q", "HEAD"]) def isValidGitDir(path): return git_dir(path) != None @@ -859,8 +882,12 @@ def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize): cmd += ["%s...@%s" % (p, revisionRange)] # Insert changes in chronological order - for line in reversed(p4_read_pipe_lines(cmd)): - changes.add(int(line.split(" ")[1])) + for entry in reversed(p4CmdList(cmd)): + if entry.has_key('p4ExitCode'): + die('Error retrieving changes descriptions ({})'.format(entry['p4ExitCode'])) + if not entry.has_key('change'): + continue + changes.add(int(entry['change'])) if not block_size: break @@ -1151,6 +1178,12 @@ class Command: self.needsGit = True self.verbose = False + # This is required for the "append" cloneExclude action + def ensure_value(self, attr, value): + if not hasattr(self, attr) or getattr(self, attr) is None: + setattr(self, attr, value) + return getattr(self, attr) + class P4UserMap: def __init__(self): self.userMapFromPerforceServer = False @@ -1316,9 +1349,10 @@ class P4Submit(Command, P4UserMap): optparse.make_option("--shelve", dest="shelve", action="store_true", help="Shelve instead of submit. Shelved files are reverted, " "restoring the workspace to the state before the shelve"), - optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int", + optparse.make_option("--update-shelve", dest="update_shelve", action="append", type="int", metavar="CHANGELIST", - help="update an existing shelved changelist, implies --shelve") + help="update an existing shelved changelist, implies --shelve, " + "repeat in-order for multiple shelved changelists") ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -1327,7 +1361,7 @@ class P4Submit(Command, P4UserMap): self.preserveUser = gitConfigBool("git-p4.preserveUser") self.dry_run = False self.shelve = False - self.update_shelve = None + self.update_shelve = list() self.prepare_p4_only = False self.conflict_behavior = None self.isWindows = (platform.system() == "Windows") @@ -1506,37 +1540,62 @@ class P4Submit(Command, P4UserMap): [upstream, settings] = findUpstreamBranchPoint() - template = "" + template = """\ +# A Perforce Change Specification. +# +# Change: The change number. 'new' on a new changelist. +# Date: The date this specification was last modified. +# Client: The client on which the changelist was created. Read-only. +# User: The user who created the changelist. +# Status: Either 'pending' or 'submitted'. Read-only. +# Type: Either 'public' or 'restricted'. Default is 'public'. +# Description: Comments about the changelist. Required. +# Jobs: What opened jobs are to be closed by this changelist. +# You may delete jobs from this list. (New changelists only.) +# Files: What opened files from the default changelist are to be added +# to this changelist. You may delete files from this list. +# (New changelists only.) +""" + files_list = [] inFilesSection = False + change_entry = None args = ['change', '-o'] if changelist: args.append(str(changelist)) - - for line in p4_read_pipe_lines(args): - if line.endswith("\r\n"): - line = line[:-2] + "\n" - if inFilesSection: - if line.startswith("\t"): - # path starts and ends with a tab - path = line[1:] - lastTab = path.rfind("\t") - if lastTab != -1: - path = path[:lastTab] - if settings.has_key('depot-paths'): - if not [p for p in settings['depot-paths'] - if p4PathStartsWith(path, p)]: - continue - else: - if not p4PathStartsWith(path, self.depotPath): - continue + for entry in p4CmdList(args): + if not entry.has_key('code'): + continue + if entry['code'] == 'stat': + change_entry = entry + break + if not change_entry: + die('Failed to decode output of p4 change -o') + for key, value in change_entry.iteritems(): + if key.startswith('File'): + if settings.has_key('depot-paths'): + if not [p for p in settings['depot-paths'] + if p4PathStartsWith(value, p)]: + continue else: - inFilesSection = False - else: - if line.startswith("Files:"): - inFilesSection = True - - template += line - + if not p4PathStartsWith(value, self.depotPath): + continue + files_list.append(value) + continue + # Output in the order expected by prepareLogMessage + for key in ['Change', 'Client', 'User', 'Status', 'Description', 'Jobs']: + if not change_entry.has_key(key): + continue + template += '\n' + template += key + ':' + if key == 'Description': + template += '\n' + for field_line in change_entry[key].splitlines(): + template += '\t'+field_line+'\n' + if len(files_list) > 0: + template += '\n' + template += 'Files:\n' + for path in files_list: + template += '\t'+path+'\n' return template def edit_template(self, template_file): @@ -1757,9 +1816,10 @@ class P4Submit(Command, P4UserMap): mode = filesToChangeExecBit[f] setP4ExecBit(f, mode) - if self.update_shelve: - print("all_files = %s" % str(all_files)) - p4_reopen_in_change(self.update_shelve, all_files) + update_shelve = 0 + if len(self.update_shelve) > 0: + update_shelve = self.update_shelve.pop(0) + p4_reopen_in_change(update_shelve, all_files) # # Build p4 change description, starting with the contents @@ -1769,7 +1829,7 @@ class P4Submit(Command, P4UserMap): logMessage = logMessage.strip() (logMessage, jobs) = self.separate_jobs_from_description(logMessage) - template = self.prepareSubmitTemplate(self.update_shelve) + template = self.prepareSubmitTemplate(update_shelve) submitTemplate = self.prepareLogMessage(template, logMessage, jobs) if self.preserveUser: @@ -1842,7 +1902,7 @@ class P4Submit(Command, P4UserMap): message = message.replace("\r\n", "\n") submitTemplate = message[:message.index(separatorLine)] - if self.update_shelve: + if update_shelve: p4_write_pipe(['shelve', '-r', '-i'], submitTemplate) elif self.shelve: p4_write_pipe(['shelve', '-i'], submitTemplate) @@ -1960,6 +2020,10 @@ class P4Submit(Command, P4UserMap): else: return False + for i in self.update_shelve: + if i <= 0: + sys.exit("invalid changelist %d" % i) + if self.master: allowSubmit = gitConfig("git-p4.allowSubmit") if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","): @@ -1970,7 +2034,7 @@ class P4Submit(Command, P4UserMap): if len(self.origin) == 0: self.origin = upstream - if self.update_shelve: + if len(self.update_shelve) > 0: self.shelve = True if self.preserveUser: @@ -2082,6 +2146,11 @@ class P4Submit(Command, P4UserMap): if gitConfigBool("git-p4.detectCopiesHarder"): self.diffOpts += " --find-copies-harder" + num_shelves = len(self.update_shelve) + if num_shelves > 0 and num_shelves != len(commits): + sys.exit("number of commits (%d) must match number of shelved changelist (%d)" % + (len(commits), num_shelves)) + # # Apply the commits, one at a time. On failure, ask if should # continue to try the rest of the patches, or quit. @@ -2352,12 +2421,6 @@ class P4Sync(Command, P4UserMap): if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False - # This is required for the "append" cloneExclude action - def ensure_value(self, attr, value): - if not hasattr(self, attr) or getattr(self, attr) is None: - setattr(self, attr, value) - return getattr(self, attr) - # Force a checkpoint in fast-import and wait for it to finish def checkpoint(self): self.gitStream.write("checkpoint\n\n") @@ -3562,7 +3625,7 @@ class P4Rebase(Command): def rebase(self): if os.system("git update-index --refresh") != 0: - die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash."); + die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up to date or stash away all your changes with git stash."); if len(read_pipe("git diff-index HEAD --")) > 0: die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash."); |