p4_shelve.py #2

  • //
  • guest/
  • shawn_hladky/
  • p4_shelve/
  • p4_shelve.py
  • View
  • Commits
  • Open Download .zip Download (10 KB)
#----------------------------------------------------------------------------
# shelve changelist in Perforce
#
#   $Id: $
#
#   Copyright 2005, Chris Stoy.  All Rights Reserved.
#
#   License:
#   This file and any derivatives or translations of it may be freely
#   copied and redistributed so long as:
#       1) This license and copyright notice are not changed.
#       2) Any fixes or enhancements are reported back to either the
#           author (cstoy@nc.rr.com).
#   and any of:
#       a) The source is redistributed with it.
#       b) The source is compiled unmodified, and instructions for finding
#           the original source are included.
#       c) The source is made available by some other means.
#       
#----------------------------------------------------------------------------

import sys
import os
import time
import shutil
import p4
import getopt

VERSION = "\np4_shelve Version 1.1  12/06/2005\n"

#----------------------------------------------------------------------------

def p4_shelve(p4c, changeListID, shelfRoot, verbose=False ):
    
    # get the data/time string we need for the branch
    ltime = time.localtime()
    datetime = time.strftime( "%Y%m%d_%H%M", ltime )

    p4User = p4c.user.replace(" ", "_")
    
    #----------------------------------------------------------------------------
    # get client spec info
    client = p4c.fetch_client()
    clientName = client["Client"]
    clientRootDir = client["Root"]
    clientView = client["View"]
    
    #----------------------------------------------------------------------------
    # get changelist we wish to shelve
    changelist = p4c.fetch_change(changeListID)
    try:
        origFiles = changelist["Files"]
    except KeyError:
        print "No files to shelve."
        return

    #----------------------------------------------------------------------------
    # save the current changelist as a numbered changelist
    origDescription = changelist["Description"]
    changelist["Description"] = "Pre-shelving changelist"
    result = p4c.save_change(changelist)[0]
    origChangeListNumber = result.split()[1]

    if verbose:
        print "Shelving changelist %s" % origChangeListNumber
    
    #----------------------------------------------------------------------------
    # get depot path for the shelve
    shelfDepotPath = "%s/%s/shelves/%s" % ( shelfRoot, p4User, datetime )

    # build the mapping of files from original depot to branch location
    branchView = []
    branchFiles = []
    for origFile in origFiles:
        # the shelve path will be the shelfRoot plus the full path of the original
        # minus the superfulus initial '/'
        filePath = origFile[2:]
        branchedFile = "%s/%s" % ( shelfDepotPath, filePath )
        branchFiles.append(branchedFile)
        mapping = "%s %s" % ( origFile, branchedFile )
        branchView.append(mapping)
        
    #----------------------------------------------------------------------------
    # Create branch spec for all files in changelist to shelve depot
    branchSpecName = "shelve_%s_%s" % ( p4User, datetime )

    if verbose:
        print "Creating shelf branch spec '%s'" % branchSpecName

    branchSpec = p4c.fetch_branch(branchSpecName)
    branchSpec["Description"] = "Shelved changelist for user %s on %s\n\n%s" % (p4c.user, time.asctime(ltime), origDescription)
    branchSpec["View"] = branchView
    p4c.save_branch(branchSpec)
    
    #----------------------------------------------------------------------------
    # Integrate using this branch spec.
    if verbose:
        print "Integrating using branch spec %s and client %s" % (branchSpecName, "//...@%s" % clientName )

    p4c.run_integrate( "-b", branchSpecName, "//...@%s" % clientName )
    
    #----------------------------------------------------------------------------
    # submit the branched files (needed to maintain correct integration history)
    branchChangeList = p4c.fetch_change()
    try:
        branchChangeList["Files"]
        branchChangeList["Description"] = "Shelved changelist for user %s on %s" % (p4c.user, time.asctime(ltime))
        result = p4c.save_submit( branchChangeList )
    except KeyError:
        # there are no files in the changelist, so just pass
        pass
    
    #----------------------------------------------------------------------------
    # copy original file contents over branched files
    for mapping in branchView:
        files = mapping.split(" ")

        origResult = p4c.run_fstat(files[0])
        origLocalFile = origResult[0]["clientFile"]
        action = origResult[0]["action"]

        result = p4c.run_where(files[1])[0]
        branchLocalFile = result["path"].replace("\\","/")

        # if file is opened for integrate/branch, we're in a real quandry.  It would be some serious work
        # to try and re-open it for branch in all the correct-ness of the original intent.
        # However, the most likely use-case for this is shelve, un-shelve, and try to re-shelve the same set of files 
        # In this case it's as simple as treating them as add/edit respectively
        if action in ("add", "branch"):
            if verbose:
                print "Adding %s" % ( branchLocalFile )
            path = branchLocalFile.rsplit("/", 1)[0];
            try:
                if verbose:
                    print "Making dir: %s" % path
                os.makedirs( path )
            except OSError:
                # ignore directory exists error
                pass
            shutil.copy2( origLocalFile, branchLocalFile )
            p4c.run_add( branchLocalFile )

        elif action in ("edit", "integrate"):
            if verbose:
                print "Copying %s to %s" % ( origLocalFile, branchLocalFile )
            p4c.run_edit( branchLocalFile )
            shutil.copy2( origLocalFile, branchLocalFile )
            
        elif action == "delete":
            if verbose:
                print "Deleting %s" % ( branchLocalFile )
            p4c.run_delete( branchLocalFile )

    # submit branched files with changes to Perforce
    branchChangeList = p4c.fetch_change()
    branchChangeList["Description"] = "Changelist shelved for user %s on %s" % (p4c.user, time.asctime(ltime))
    result = p4c.save_submit( branchChangeList )
    
    #----------------------------------------------------------------------------
    # revert files in main branch and delete the pending changelist
#    for mapping in branchView:
#        files = mapping.split(" ")
#        result = p4c.run_revert(files[0])[0]
        
#    p4c.run_change( "-d", origChangeListNumber )

    # all done
    print "Changelist Shelved using branch %s" % branchSpecName
    

#----------------------------------------------------------------------------
# prints the usage message. If short is true, prints abreviated
# help message.
#----------------------------------------------------------------------------
def usage(short=True):
    print "Usage: p4_shelve [options]\n"
    if short :
        print "Try 'p4_shelve --help' for more information."
    else:
        print "Shelves a changelist for later editing."
        print ""
        print "Shelving a changelist will create a branch of the files in the"
        print "current changelist and move all the changes to that branch. It"
        print "will then revert the files in the current changelist while preserving"
        print "any changes in the branch."
        print ""
        print "Use 'p4_unshelve' to restore a shelved changelist."
        print ""
        print "Options:"
        print "  -h, --help         Prints this help message"
        print "  -v, --verbose      Prints verbose messages while shelving"
        print "  -p, --port         Perforce Port depot is on"
        print "  -c, --client       Perforce Client that maps the depot files"
        print "  -u, --user         Perforce User that owns the client"
        print "  -P, --password     Perforce Password for the user"
        print "  -e, --changelist   Perforce Change List ID to shelve"
        print "  -s, --shelfroot    Depot path where the shelved files should go."
        print "  -r, --keepopen     Files in pending changelist will not be reverted shelved."
        print ""
        print "Note: Uses the default P4 settings for Perforce options not supplied."
        print "\nReport bugs to Chris Stoy (cstoy@nc.rr.com)"
        
    print VERSION
        
#-----------------------------------------------------------
# Main entry point
#-----------------------------------------------------------
if __name__ == "__main__":

    options = []
    input = []
    
    for x in sys.argv[1:]:
        if x.startswith("-"):
            options.append(x)
        else:
            input.append(x)

    try:
        opts, args = getopt.getopt( options, "p:c:u:P:e:s:hv", ["port=", "client=", "user=", "password=", "changelist=", "shelfroot=", "help", "verbose"] )

    except getopt.GetoptError, intr:
        print "Argument error: ", intr
        usage()
        sys.exit(2)
        
    shelfRoot = "//depot/shelves"   # depot path to where the shelves are stored for each user
    changeListID = ""       # changelist number for changelist we wish to shelve, or "" to shelve default
    verbose = False

    p4c = p4.P4()
    p4c.parse_forms()

    # we got our options. 
    for o, a in opts:
        if o in ("-h", "--help"):
            usage(False)
            sys.exit(0)
        if o in ("-p", "--port"):
            p4c.port = a
        if o in ("-c", "--client"):
            p4c.client = a
        if o in ("-u", "--user"):
            p4c.user = a
        if o in ("-P", "--password"):
            p4c.password = a
        if o in ("-e", "--changelist"):
            if a != "default":
                changeListID = a
        if o in ("-v", "--verbose"):
            verbose = True
        if o in ("-s", "--shelfroot"):
            shelfRoot = a

    if len(input) > 0:
        print "Incorrect Usage."
        print input
        usage()
        sys.exit(2)
        
    # connect to P4
    p4c.connect()
    
    if verbose:
        print "Settings:"
        print "\tP4 Port = %s" % p4c.port
        print "\tP4 Client = %s" % p4c.client
        print "\tP4 User = %s" % p4c.user
        print "\tChangeListID = %s" % changeListID
        print "\tShelf Root = %s" % shelfRoot

    # do the shelving
    try:    
        p4_shelve(p4c, changeListID, shelfRoot, verbose)
    
    except p4.P4Error:
        print "P4 Error:"
        for e in p4c.errors:
            print "\t%s" % e
            
            
# Change User Description Committed
#2 5362 Shawn Hladky Chipping away at the API changes
#1 5285 Shawn Hladky Integrating p4_shelve from Chris Stoy.
I intend to make the following fixes/enhancements:

If you shelve a named changelist, and also have opend files in your defualt changelist, then the files in the default changelist will be submitted along with the shelve.

Opend files with the same filename, but different directories can not be shelved together

Filepaths with a space in the filename will not be shelved correctly.

A file opened for Add will left writable in the local workspace.  When you un-shelve, and you have noclobber set on your client spec, you will get a "Can't clobber writable file" error.
//guest/chris_stoy/p4_shelve/p4_shelve.py
#3 5228 Chris Stoy - Fixed bug where the integration for the branch was using the latest revision instead of the revision of the file being edited in the workspace.
 This could cause problems if reintegrating back into the mainline after the files have been modified (which is likely.)  Fix was to specify the client in the file spec for the integrate command.
#2 5209 Chris Stoy - updated default depot for shelves to //depot/shelves
- added first readme file
#1 5208 Chris Stoy First version of these files.
 Provides the shelve, unshelve, and setup script for making stand-alone Exes.

Requires P4Python to run.