Created
March 3, 2014 13:19
-
-
Save jamieechlin/9324727 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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