Skip to content

Instantly share code, notes, and snippets.

@jamieechlin
Created March 3, 2014 13:19
Show Gist options
  • Save jamieechlin/9324727 to your computer and use it in GitHub Desktop.
Save jamieechlin/9324727 to your computer and use it in GitHub Desktop.
#!/app/Subversion/linux/bin/python
import sys
import os
import commands
import ast
import re
"""
This is a program that will fix esvn:roots on a repository. It will work from the file
conf/esvn_hints and will ensure that these missing roots are applied to the property and tr.py
re-run to the next stage
"""
def execute_cmd(cmd):
"""
Rather than duplicating this code everywhere wanted to create a standard function
returns op. If the command fails will report the message and then return status
"""
(status, op) = commands.getstatusoutput(cmd)
if status != 0:
print op
print "Unable to execute %s" % cmd
return (status, op)
def capture_esvn_roots_property(revision, path):
"""
extracts the contents of esvn:roots for the specified revision and path
Returns this as the string original_property_contents
"""
cmd = "/app/Subversion/linux/bin/svn pg --revprop -r%s esvn:roots file://%s" % (revision, path)
(status, original_property_contents) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
if ( original_property_contents == "" ):
print "esvn:roots property empty on revision %s in repository %s" % (revision, path)
sys.exit(1)
return original_property_contents
def read_esvn_hints_file(path):
"""
Reads the esvn_hints file, held in path/conf, returns the dictionary hints with the revised roots
"""
esvn_hints_file = "%s/conf/esvn_hints" % path
esvn_hints = open(esvn_hints_file)
esvn_hint_contents = esvn_hints.readlines()
hints = {}
for line in esvn_hint_contents:
line = line.strip('\n')
if ( re.search("^#", line) or ( line == "" ) or ( line == "\s+$" )):
next
else:
( revision, roots ) = line.split(":")
hints[int(revision)] = roots
return hints
def get_last_rev(path):
"""
Simply returns the esvn:last_rev property value, if not set returns HEAD
"""
cmd = "/app/Subversion/linux/bin/svn pg --revprop -r0 esvn:last_rev file://%s" % path
(status, last_rev) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
if ( last_rev == "" ):
cmd = "/app/Subversion/linux/bin/svnlook youngest %s" % path
(status, last_rev) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
return last_rev
def modify_property_contents(path):
"""
This is the routine that will modify the existing property with the new roots in hints
"""
hints = {}
hints = read_esvn_hints_file(path)
last_rev = get_last_rev(path)
# Work out the start point for making teh modifications. This will allow us to reduce work in re-applying
# where not needed.
first_missing_root = find_first_missing_root(path, hints, last_rev)
if ( first_missing_root == last_rev):
print "All roots already applied, no action\n"
sys.exit(0)
print "Need to apply from revision %s\n" % first_missing_root
# Purge the esvn:roots property from revision first_missing_root to HEAD
input = raw_input("About to purge the property esvn:roots from revisions %s to HEAD. Do you want to Continue (a No answer will simply skip the purge step)? (y/n)" % (first_missing_root + 1))
if ( input == "y" ):
# Let's purge the revisions, but don't purge the one we need to change.
# Note, if they opt not to purge the properties we still continue.
status = purge_esvn_roots(first_missing_root + 1, last_rev)
# So we have a list of changes in the dictionary (hints, we have a starting revision (first_missing_root)
# And a path (path). Let's go and modify the properties, and progress tr.py
status = apply_property_changes(first_missing_root, hints, path)
def patch_revisions(from_revision, revision, path):
"""
We have just patched the the esvn:roots property and need to run tr.py to the next revision
"""
print "Running tr.py for revisions %s to %s on %s" % (from_revision, revision, path)
cmd = "/app/Subversion/linux/bin/python /home/svnadm/unity/trunk/hook_scripts/esvn/tr.py %s %s %s" % (path, from_revision, revision)
(status, op) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
# Uncomment if necessary, but this generates a lot of noise
# else:
# print op
return status
def apply_property_changes(first_missing_root, hints, path):
"""
Starting from revision first_missing_root it will modify the esvn:roots property with the missing roots
held in hints
"""
from_revision = 0
for revision in sorted(hints.iterkeys()):
if ( revision < first_missing_root ):
continue
else:
if ( from_revision != 0 ):
# We now need to apply revisions via tr.py to the current revision
returned = patch_revisions(from_revision, revision, path)
current_revision_roots = hints[revision]
new_roots = current_revision_roots.rsplit(",") # The roots are listed in a comma delimitted format
original_property_contents = capture_esvn_roots_property(revision, path)
esvn_roots = ast.literal_eval(original_property_contents)
for missing_root in new_roots:
missing_root = tidy_missing_root(missing_root) # Simply strips quotes
found = 0
for existing_root, created in esvn_roots.iteritems():
if ( missing_root == existing_root ):
found = 1
next
if ( found == 0 ):
# The root missing_root needs to be added
print "The root %s is missing from revision %s\n" % ( missing_root, revision)
esvn_roots = apply_modified_property(revision, path, missing_root, esvn_roots)
# We should now have a modified hints dictionary
# Lets use this to apply to the property and then run catch-up for the missing revisions
revised_property_contents = ""
revised_property_contents = esvn_roots
# print "**********\nChanging revision %s from\n%s\nto\n%s\n**********" % (revision, original_property_contents, revised_property_contents)
returned = update_property(path, revision, revised_property_contents)
from_revision = int(revision) + 1
# Have now applied all of the updates to the last revision, so let's play through from the (from_revision)
# to HEAD
revision = "HEAD"
returned = patch_revisions(from_revision, revision, path)
return 0
def update_property(path, revision, revised_property_contents):
"""
Applies the new property details (revised_property_contents) to revision (revision) in the repository (path)
"""
cmd = "/app/Subversion/linux/bin/svn ps --revprop -r%s esvn:roots \"%s\" file://%s" % (revision, revised_property_contents, path)
(status, op) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
else:
print op
return status
def apply_modified_property(revision, path, missing_root, esvn_roots):
print "Applying missing root %s to esvn:roots on revision %s" % (missing_root, revision)
created_dictionary_item = {}
created_dictionary_item["created"] = int(revision)
esvn_roots[missing_root] = created_dictionary_item
return esvn_roots
def purge_esvn_roots(first_missing_root, last_rev):
"""
The tr.py script looks forward as well as current version so before we edit the property and re-run tr.py
we need to purge existing revisions
"""
for purge_revision in range( int(first_missing_root), int(last_rev) + 1 ):
cmd = "/app/Subversion/linux/bin/svn pd --revprop -r%s esvn:roots file://%s" % ( str(purge_revision), str(path) )
(status, op) = execute_cmd(cmd)
if status != 0:
sys.exit(1)
else:
print op
return 0
def tidy_missing_root(missing_root):
"""
Just moving some code out of a for loop to make it more readable
"""
missing_root = re.sub("'\s+", "'", missing_root)
missing_root = re.sub("\s+'", "'", missing_root)
missing_root = re.sub("'", "", missing_root)
return missing_root
def find_first_missing_root(path, hints, last_rev):
"""
Returns the revision id of the first missing root and returns this
"""
first_revision = 0
for revision in sorted(hints.iterkeys()):
if ( first_revision == 0 ):
first_revision = revision
current_revision_roots = hints[revision]
new_roots = current_revision_roots.rsplit(",")
original_property_contents = capture_esvn_roots_property(revision, path)
esvn_roots = ast.literal_eval(original_property_contents)
for missing_root in new_roots:
missing_root = tidy_missing_root(missing_root) # Simply strips quotes
found = 0
for existing_root, created in esvn_roots.iteritems():
if ( missing_root == existing_root ):
found = 1
print "*** %s (revision %s) already exists, no action ***\n %s: %s\n=============================================\n" % (missing_root, revision, existing_root, created)
break
if ( found == 0 ):
# This is the first missing root. Need to purge all properties from this point
# forward
print "The root %s is missing from revision %s\n" % ( missing_root, revision)
return revision
# If we reach here then all of the roots are applied, return the last_rev revision, i.e. all changes applied
return last_rev
if __name__ == "__main__":
if len (sys.argv) != 2:
print "usage ./fix_esvn_roots.py path"
sys.exit(1)
path = sys.argv[1]
if not ( os.path.exists(path)):
print "%s repository does not exist\n" % path
sys.exit(1)
esvn_hints_file = "%s/conf/esvn_hints" % path
if not ( os.path.exists(esvn_hints_file)):
print "%s The esvn_hints file does not exist\n" % esvn_hints_file
sys.exit(1)
returned = modify_property_contents(path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment