#!/usr/bin/env python '''Perforce publish/catchup implementation See http://robertcowham.com/blog/index.cgi/scm/p4_auto_merge.html ''' # Copyright (c) 2006 Qualcomm # Miki Tebeka from os.path import join, isfile from os import environ from time import ctime from sys import platform from p4 import P4, P4Error from p4v_common import fix_path, new_change, branch_exists, gen_p4, \ branch_source, branch_target SUBMIT, CLOSE, UNDO = 0, 1, 2 def ask(change): '''Ask user what to do with publish change - Created changelist number Return SUBMIT, CLOSE or UNDO ''' question = "What now? [(S)ubmit/(C)lose/(U)ndo] " % change while 1: try: ans = raw_input(question).strip().lower() if not ans: continue if ans in ["s", "submit"]: return SUBMIT elif ans in ["c", "close"]: return CLOSE elif ans in ["u", "undo"]: return UNDO if ans in ["y", "yes", "ok"]: print "Bad answer" except KeyboardInterrupt: return 0 def publish(p4, branch, submit=1, log=None): '''A publish operation p4 - P4 object branch - Branchspec to use submit - Do final submit operation log - Logging function ''' log("checking the no-one else is working on branch") src = branch_source(p4, branch) if has_opened_files(p4, src): raise P4Error("Someone else is working on %s" % src) return _operate(p4, branch, 0, submit, log) def catchup(p4, branch, submit=1, log=None): '''A catchup operation p4 - P4 object branch - Branchspec to use submit - Do final submit operation log - Logging function ''' target = branch_target(p4, branch) if has_opened_files(p4, target): raise P4Error("Please submit all files before catching up") return _operate(p4, branch, 1, submit, log) def is_synced(p4, branch): '''Check if target is synced to source p4 - P4 object branch - Branchspec to use ''' return len(p4.run_integrate("-n", "-b", branch)) == 0 def has_opened_files(p4, path): '''Check if there are opened files in path''' return len(p4.run_opened("-a", path)) > 0 def _operate(p4, branch, is_catchup, submit, log): '''Real workhorse p4 - P4 object branch - Branchspec to use is_catchup - Is operation a catchup? submit - Do final submit operation log - Logging function ''' change = "" # Change number cleanup = 1 # Cleanup flag if not log: log = lambda x: 1 try: log("creating new changelist") # Create a new changelist for this submit change = new_change(p4, "%s publishing" % p4.user) log("changelist %s created" % change) # Check if private branch is synched log("checking if private branch is synchronized with integration") private_synced = is_synced(p4, branch) if is_catchup: if private_synced: log("nothing to do") return else: # Publish if not private_synced: raise P4Error("private branch not synchroized to integration " "branch") # Integrate from private to integration branch if is_catchup: log("merging integration branch to private branch") p4.run_integrate("-c", change, "-b", branch) else: log("merging private branch to integration branch") p4.run_integrate("-c", change, "-b", branch, "-r") if not p4.run_opened("-c", change): log("nothing to do") p4.run_change("-d", change) return # Resolve what we can log("resolving what we can") p4.run_resolve("-as") # Ask user log("Please check changelist %s and resolve conflicts" % change) if not submit: cleanup = 0 return change ans = ask() if ans == CLOSE: cleanup = 0 elif ans == SUBMIT: log("submitting change") p4.run_submit("-c", change) cleanup = 0 else: # Undo # Since we didn't unset "cleanup" the "finally" clause will clean pass return change finally: if cleanup and change: # Something went wrong try: # This will raise an exception if changes does not exist p4.fetch_change(change) log("undoing changes") p4.run_revert("-c", change, "//...") p4.run_change("-d", change) except P4Error: pass def main(argv=None): if not argv: import sys argv = sys.argv[1:] from optparse import OptionParser parser = OptionParser("usage: %prog [options] BRANCH") parser.add_option("-u", help="User name", dest="user") parser.add_option("-c", help="Client name", dest="client") parser.add_option("-p", help="Port (server:port)", dest="port") parser.add_option("-l", help="label publish", dest="label") parser.add_option("-v", help="be verbose", dest="verbose", action="store_true", default=0) parser.add_option("--catchup", help="do a catchup", dest="catchup", action="store_true", default=0) parser.add_option("--no-submit", help="don't submit", dest="submit", action="store_false", default=1) opts, args = parser.parse_args(argv) if len(args) != 1: parser.error("wrong number of arguments") # Will exit def log(msg): if opts.verbose: print msg def get(name, val, envkey): if val: return val if envkey in environ: return environ[envkey] raise KeyError(name) try: p4 = gen_p4(opts) branch = args[0] if not branch_exists(p4, branch): raise P4Error("branch %s does not exist" % branch) # Update is the other way publish without locking if opts.catchup: catchup(p4, branch, opts.submit, log) else: publish(p4, branch, opts.submit, log) except P4Error, e: raise SystemExit("error: %s" % e) if __name__ == "__main__": main()