Write meta files for dirs; sort deleted directories below any child files within a changeset; refactor guid_path for clarity

This commit is contained in:
Stephen Palmer 2014-09-11 08:14:51 -05:00
Родитель f2edf0d222
Коммит 4b90786b4a
1 изменённых файлов: 73 добавлений и 44 удалений

Просмотреть файл

@ -33,7 +33,7 @@ if sys.platform == "win32":
query_assetversions="""SELECT av.created_in AS changeset, guid2hex(a.guid) AS guid, guid2hex(get_asset_guid_safe(av.parent)) AS parent, av.name, av.assettype FROM assetversion av, asset a WHERE av.asset=a.serial AND av.created_in <= %d ORDER BY av.serial"""
# query for full asset version details of a given changeset to translate into git commits
query_assetversiondetails="""SELECT vc.changeset, cs.description AS log, extract(epoch FROM commit_time)::int AS date, a.serial, guid2hex(a.guid) AS guid, av.name, guid2hex(get_asset_guid_safe(av.parent)) AS parent, av.assettype, av.serial AS version FROM variant v, variantinheritance vi, variantcontents vc, changeset cs, changesetcontents cc, ASsetversion av, ASset a WHERE v.name = 'work' AND vi.child = v.serial AND vc.variant = vi.parent AND cs.serial=vc.changeset AND cs.serial=cc.changeset AND cc.assetversion=av.serial AND av.asset=a.serial AND vc.changeset = %d ORDER BY av.serial"""
query_assetversiondetails="""SELECT vc.changeset, cs.description AS log, extract(epoch FROM commit_time)::int AS date, a.serial, guid2hex(a.guid) AS guid, av.name, guid2hex(get_asset_guid_safe(av.parent)) AS parent, at.description as assettype, av.serial AS version FROM variant v, variantinheritance vi, variantcontents vc, changeset cs, changesetcontents cc, assetversion av, asset a, assettype at WHERE v.name = 'work' AND vi.child = v.serial AND vc.variant = vi.parent AND cs.serial=vc.changeset AND cs.serial=cc.changeset AND cc.assetversion=av.serial AND av.asset=a.serial AND av.assettype=at.serial AND vc.changeset = %d ORDER BY av.serial"""
# gets a user list with associated email addresses, if any
query_users="""select person.serial, person.username, split_part(pg_shdescription.description, ':'::text, 1) AS realname, split_part(pg_shdescription.description, ':'::text, 2) AS email FROM person LEFT JOIN pg_user ON person.username = pg_user.usename LEFT JOIN pg_shdescription ON pg_user.usesysid = pg_shdescription.objoid"""
@ -42,7 +42,7 @@ query_users="""select person.serial, person.username, split_part(pg_shdescriptio
query_changesets="""SELECT cs.serial as id, cs.description, cs.commit_time as date, CASE WHEN p.email = 'none' OR p.email IS NULL THEN ' <' || p.username || '@' || p.username || '>' ELSE COALESCE(p.realname, p.username) || ' <' || p.email || '>' END AS author FROM (""" + query_users + """) AS p, changeset cs WHERE p.serial = cs.creator AND cs.serial > %d"""
# list of all large object streams associated with a given asset version
query_streams="""SELECT assetversion,tag,lobj FROM stream, assetcontents WHERE stream = lobj AND assetversion = %d"""
query_streams="""SELECT assetversion,tag,lobj FROM stream, assetcontents WHERE stream = lobj AND tag <> 'assetRsrc' AND assetversion = %d"""
# function called by trigger for auto update
query_func_auto_update="""CREATE OR REPLACE FUNCTION git_unity_as ()
@ -72,12 +72,12 @@ def export_data(out, data):
out.write(data)
# Helper function to write the data header + buffer binary data for a given stream to stdout
def inline_data(out, stream, path, code = 'M', mode = '644', nodata = False):
def inline_data(out, stream, path, code = 'M', mode = '644', nodata = False, data = None):
out.write("%s %s inline \"%s\"\n" % (code, mode, path))
if(nodata == True):
out.write("data 1\n")
out.write("-\n")
# data provided, do not lookup stream
if data != None:
export_data(out, data)
return
obj=psycopg2.extensions.lobject(conn, stream, 'b')
@ -86,6 +86,10 @@ def inline_data(out, stream, path, code = 'M', mode = '644', nodata = False):
out.write("data %d\n" % size)
bytes_read=0
if(nodata == True):
out.write("stream %d\n" % stream)
return
while bytes_read < size:
buff_size = min(size - bytes_read, 2048)
out.write(obj.read(buff_size))
@ -97,22 +101,17 @@ def new_guid_item(name, parent):
return { 'name': name, 'parent': parent }
# Get the full path for a given guid object, or move/rename an existing object
def guid_path(guid, new_parent = None, name = None):
if(guid_map.has_key(guid) == False):
if(name is not None):
def guid_path(guid, new_parent = None, new_name = None):
if guid_map.has_key(guid):
if new_parent != None:
guid_map[guid]['parent'] = new_parent
if new_name != None:
guid_map[guid]['name'] = new_name
else:
# Special case for ProjectSettings/*.asset
if(name.endswith(".asset") and new_parent is None):
parent = settings_guid
else:
parent = new_parent
guid_map[guid] = new_guid_item(name, parent)
else:
return "";
elif(new_parent is not None):
guid_map[guid] = new_guid_item(name, new_parent)
if(new_name.endswith(".asset") and new_parent is None):
new_parent = settings_guid
guid_map[guid] = new_guid_item(new_name, new_parent)
# recursive function to build a qualified path for a given guid
def build_path(parent_guid, path = ""):
@ -131,20 +130,24 @@ def guid_path(guid, new_parent = None, name = None):
return build_path(guid)
# Get a list of large object id's and associated tags for a given asset version
def get_streams(asset_version):
def get_streams(asset_type, asset_guid, asset_version):
stream_ar = []
if asset_type == 'dir':
meta="""fileFormatVersion: 2\nguid: %s\nfolderAsset: yes\nDefaultImporter:\n userData: \n""" % asset_guid
stream_ar.append({ 'type': asset_type, 'tag': 'asset.meta', 'data': meta })
else:
cur.execute(query_streams % asset_version);
streams = cur.fetchall()
stream_ar = []
for stream in streams:
stream_ar.append({ 'tag': stream['tag'], 'lobj': stream['lobj'] })
stream_ar.append({ 'type': asset_type, 'tag': stream['tag'], 'lobj': stream['lobj'] })
return stream_ar
# Get a list of commands to be sent to git fast-import
def get_ops(asset_name, asset_version, asset_guid, parent_guid):
def get_ops(asset_type, asset_name, asset_version, asset_guid, parent_guid):
ops=[]
path=''
streams = get_streams(asset_version)
streams = get_streams(asset_type, asset_guid, asset_version)
def create_op(op_name, op_path, stream_tag, stream_id):
if(stream_tag == "asset.meta"):
@ -157,7 +160,7 @@ def get_ops(asset_name, asset_version, asset_guid, parent_guid):
if(guid_item['parent'] != parent_guid or guid_item['name'] != asset_name):
if(guid_item['parent'] != trash_guid):
for stream in streams:
ops.append(create_op('D', old_path, stream['tag'], stream['lobj']))
ops.append(create_op('D', old_path, stream['tag'], ''))
path=guid_path(asset_guid, parent_guid, asset_name)
else:
@ -167,6 +170,9 @@ def get_ops(asset_name, asset_version, asset_guid, parent_guid):
if(parent_guid != trash_guid):
for stream in streams:
if stream['type'] == 'dir':
ops.append(create_op('dir', path, stream['tag'], stream['data']))
else:
ops.append(create_op('M', path, stream['tag'], stream['lobj']))
return ops
@ -177,6 +183,24 @@ def get_initial_changeset():
cur.execute(query)
return int(cur.fetchone()['serial'])
def sort_versions(versions):
lastidx = len(versions) - 1;
# Move folders to delete below any of their children
for x in range(0, lastidx):
parent = versions[x]
if parent['parent'] != trash_guid:
continue
for y in range(lastidx, x, -1):
child_guid = versions[y]['guid']
if guid_map.has_key(child_guid) and guid_map[child_guid]['parent'] == parent['guid']:
del versions[x]
versions.insert(y, parent)
break
return versions
def git_export(out, last_mark = 0, opts = { 'no-data': False }):
if(isinstance(last_mark, dict)):
@ -227,22 +251,27 @@ def git_export(out, last_mark = 0, opts = { 'no-data': False }):
# emit file operations and version data for the current changeset
cur.execute(query_assetversiondetails % mark)
versions = cur.fetchall()
versions = sort_versions(cur.fetchall())
ops = []
for version in versions:
ops = get_ops(version['name'], version['version'], version['guid'], version['parent'])
ops = get_ops(version['assettype'], version['name'], version['version'], version['guid'], version['parent'])
for op in ops:
op_name=op[0]
path=op[1]
stream=op[2]
def Dir():
inline_data(out, -1, path, data=stream)
def M():
inline_data(out, stream, path, nodata=opts['no-data'])
def D():
out.write("D %s\n" % path)
options = { 'M': M, 'D': D }
options = { 'M': M, 'D': D, 'dir': Dir }
options[op_name]()
edited_comment = comment.replace('\n', ' ')