Files
2025-09-29 00:52:08 +02:00

195 lines
5.2 KiB
Python
Executable File

#!/usr/bin/env python
import os
import sys
import getopt
import marshal
import logging
P4_PORT_AND_USER = ' '
def p4( command ):
"""
Run a perforce command line instance and marshal the
result as a list of dictionaries.
"""
commandline = 'p4 %s -G %s' % (P4_PORT_AND_USER, command)
logging.debug( '%s' % commandline )
stream = os.popen( commandline, 'rb' )
entries = []
try:
while 1:
entry = marshal.load(stream)
entries.append(entry)
except EOFError:
pass
code = stream.close()
if None != code:
raise IOError( "Failed to execute %s: %d" % (commandline, int(code)) )
return entries
def deleteFile(name):
"""
Deletes the file from the repository.
"""
p4( 'delete "%s"' % name )
def backRevision(name, revision, force):
"""
Given a file and a revision number, tries to go back
one revision in time.
"""
targetRevision = revision - 1
headAction = p4( 'fstat "%s#%d"' % (name, targetRevision))[0]['headAction']
if 'delete' == headAction:
if laterRevisionExists(name, revision) and not force:
logging.warn("%s has new edits since this change list was submitted. Skipping automated delete; You must delete manually." % (name))
else:
deleteFile(name)
else:
syncDoActionAndResolve(name, "edit", int(revision), force)
def laterRevisionExists(name, revision):
headRevision = p4( 'fstat "%s"' % (name))[0]['headRev']
return int(headRevision) > revision
def recoverFile(name, revision, force):
"""
Adds a deleted file back to the repository.
"""
syncDoActionAndResolve(name, "add", int(revision), force)
def syncDoActionAndResolve(name, action, revision, force):
p4( 'sync "%s#%d"' % (name, revision -1) )
p4( '%s "%s"' % (action, name) )
p4( 'sync "%s"' % name )
headRevision = int(p4( 'fstat "%s"' % (name))[0]['headRev'])
logging.debug("revision = %d" % (revision))
logging.debug("headRevision = %d" % (headRevision))
if laterRevisionExists(name, revision) and not force:
logging.warn("%s has new edits since this change list was submitted. Skipping automated resolve; You must resolve manually." % (name))
else:
p4( 'resolve -ay "%s"' % name )
def revertChangelist( changelistNumber, force ):
"""
Steps through the whole changelist file by file and looks at
the last action taken and then tries to go back one step.
"""
logging.debug( 'Trying to revert the changelist %d' % changelistNumber )
description = p4( 'describe -s %d' % changelistNumber )[0]
infos = []
counter = 0
try:
while 1:
name = description[ 'depotFile%d' % counter ]
action = description[ 'action%d' % counter ]
revision = int(description[ 'rev%d' % counter ])
infos.append( (name, action, revision) )
counter += 1
except KeyError:
pass
for name, action, revision in infos:
logging.debug( 'Processing %s#%d' % (name, revision) )
if 'add' == action:
deleteFile(name)
if 'edit' == action:
backRevision(name, revision, force)
if 'delete' == action:
recoverFile(name, revision, force)
if 'branch' == action or 'integrate' == action:
if 1 == revision:
deleteFile(name)
else:
backRevision(name, revision, force)
def main(argv):
"""
Usage: p4revert.py [options] <changelist>
Options:
-v : verbose
-f : force
-c client : perforce client
-p port : perforce port
-u user : perforce user
"""
try:
options, arguments = getopt.getopt(argv, 'c:p:u:vf')
except getopt.GetoptError:
print 'Error parsing arguments'
print main.__doc__
return 1
# Default tweakable values for the options.
verbose = False
force = False
client = ''
port = ''
user = ''
# Loop through all the options
for o,a in options:
if '-v' == o:
verbose = True
if '-c' == o:
client = a
if '-p' == o:
port = a
if '-u' == o:
user = a
if '-f' == o:
force = True
if len(arguments) != 1:
print 'Must give one changelist number'
print main.__doc__
return 1
try:
changelistNumber = int(arguments[0])
except ValueError:
print 'Changelist number must be a number!'
print main.__doc__
return 1
global P4_PORT_AND_USER
if len(client):
P4_PORT_AND_USER += ' -c %s ' % client
if len(port):
P4_PORT_AND_USER += ' -p %s ' % port
if len(user):
P4_PORT_AND_USER += ' -u %s ' % user
# At this point we're all done with the options! Now to the real code.
if verbose:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(levelname)-7s: %(message)s' )
else:
logging.basicConfig(
level=logging.INFO, format='%(message)s' )
revertChangelist( changelistNumber, force )
logging.info( 'Revert of %d done.' % changelistNumber )
results = p4 ( "resolve -n" )
for result in results:
code = result['code']
if code == 'stat':
logging.warning("%s must be resolved." % (result['fromFile']))
elif code == 'error':
logging.info("Change list reverted and files are ready for submit.")
return 0
if __name__ == '__main__':
# This is just the main stub trick that makes the script act like a regular
# unix application. We return 1 for errors and 0 for success.
sys.exit( main(sys.argv[1:]) )