publish.py #2

  • //
  • guest/
  • miki_tebeka/
  • p4vaddins/
  • main/
  • publish.py
  • View
  • Commits
  • Open Download .zip Download (6 KB)
#!/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 <[email protected]>

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()
# Change User Description Committed
#2 5382 Miki Tebeka * Start of UNIX - File format and #!
* getvalue returns string (not unicode in some cases)
#1 5280 Miki Tebeka Initial checkin of project