SVN: UX improvements #136
@ -177,7 +177,7 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
||||
|
||||
missing_file_allowed = True
|
||||
|
||||
revision: IntProperty()
|
||||
revision: IntProperty(default=0)
|
||||
|
||||
def invoke(self, context, event):
|
||||
file_entry = context.scene.svn.get_repo(
|
||||
@ -198,18 +198,24 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
||||
"Cancelled: You have local modifications to this file. You must revert or commit it first!")
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
["svn", "up", f"-r{self.revision}",
|
||||
f"{self.file_rel_path}", "--accept", "postpone"],
|
||||
use_cred=True
|
||||
)
|
||||
self.svn_download_file_revision(context, self.file_rel_path, self.revision)
|
||||
|
||||
self.report({'INFO'},
|
||||
f"Checked out revision {self.revision} of {self.file_rel_path}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def svn_download_file_revision(self, context, svn_file_path: str, revision=0):
|
||||
commands = ["svn", "up", f"{self.file_rel_path}", "--accept", "postpone"]
|
||||
if self.revision > 0:
|
||||
commands.insert(2, f"-r{self.revision}")
|
||||
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
commands,
|
||||
use_cred=True
|
||||
)
|
||||
|
||||
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
||||
file_entry['revision'] = self.revision
|
||||
latest_rev = repo.get_latest_revision_of_file(self.file_rel_path)
|
||||
@ -229,13 +235,14 @@ class SVN_OT_restore_file(May_Modifiy_Current_Blend, Operator):
|
||||
|
||||
missing_file_allowed = True
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
def svn_revert(self, context, svn_file_path):
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
["svn", "revert", f"{self.file_rel_path}"]
|
||||
["svn", "revert", f"{svn_file_path}"]
|
||||
)
|
||||
|
||||
f = self.get_file(context)
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.svn_revert(context, self.file_rel_path)
|
||||
return {"FINISHED"}
|
||||
|
||||
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
||||
@ -250,15 +257,35 @@ class SVN_OT_revert_file(SVN_OT_restore_file):
|
||||
|
||||
missing_file_allowed = False
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
super()._execute(context)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_warning_text(self, context) -> str:
|
||||
return "You will irreversibly and permanently lose the changes you've made to this file:\n " + self.file_rel_path
|
||||
|
||||
|
||||
class SVN_OT_revert_and_update(SVN_OT_download_file_revision, SVN_OT_revert_file):
|
||||
"""Convenience operator for the "This file is outdated" warning message. Normally, these two operations should be done separately!"""
|
||||
bl_idname = "svn.revert_and_update_file"
|
||||
bl_label = "Revert And Update File"
|
||||
bl_description = "The currently opened .blend file has a newer version available on the remote repository. Click to PERMANENTLY DISCARD local changes to this file and update it to the latest revision. Cannot be undone"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
missing_file_allowed = False
|
||||
|
||||
def invoke(self, context, event):
|
||||
return super(May_Modifiy_Current_Blend, self).invoke(context, event)
|
||||
|
||||
def get_warning_text(self, context) -> str:
|
||||
if self.get_file(context).status != 'normal':
|
||||
return "You will irreversibly and permanently lose the changes you've made to this file:\n " + self.file_rel_path
|
||||
else:
|
||||
return "File will be updated to latest revision."
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.svn_revert(context, self.file_rel_path)
|
||||
self.svn_download_file_revision(context, self.file_rel_path, self.revision)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SVN_OT_add_file(SVN_Operator_Single_File, Operator):
|
||||
bl_idname = "svn.add_file"
|
||||
bl_label = "Add File"
|
||||
@ -411,15 +438,13 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
|
||||
self.execute_svn_command(context, ["svn", "cleanup"])
|
||||
repo.reload_svn_log(context)
|
||||
|
||||
Processes.kill('Status')
|
||||
Processes.kill('Log')
|
||||
Processes.kill('Commit')
|
||||
Processes.kill('Update')
|
||||
Processes.kill('Authenticate')
|
||||
Processes.kill('Activate File')
|
||||
|
||||
Processes.start('Status')
|
||||
Processes.start('Log')
|
||||
Processes.restart('Status')
|
||||
Processes.restart('Log')
|
||||
|
||||
self.report({'INFO'}, "SVN Cleanup complete.")
|
||||
|
||||
@ -428,13 +453,14 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
|
||||
|
||||
registry = [
|
||||
SVN_OT_update_single,
|
||||
SVN_OT_download_file_revision,
|
||||
SVN_OT_revert_file,
|
||||
SVN_OT_revert_and_update,
|
||||
SVN_OT_restore_file,
|
||||
SVN_OT_unadd_file,
|
||||
SVN_OT_revert_file,
|
||||
SVN_OT_download_file_revision,
|
||||
SVN_OT_add_file,
|
||||
SVN_OT_unadd_file,
|
||||
SVN_OT_trash_file,
|
||||
SVN_OT_remove_file,
|
||||
SVN_OT_cleanup,
|
||||
SVN_OT_resolve_conflict,
|
||||
SVN_OT_cleanup,
|
||||
]
|
||||
|
@ -214,6 +214,7 @@ class ProcessManager:
|
||||
def processes(self):
|
||||
# I tried to implement this thing as a Singleton that inherits from the `dict` class,
|
||||
# I tried having the `processes` dict on the class level,
|
||||
# I tried having it on the instance level,
|
||||
# and it just refuses to work properly. I add an instance to the dictionary,
|
||||
# I print it, I can see that it's there, I make sure it absolutely doesn't get removed,
|
||||
# but when I try to access it from anywhere, it's just empty. My mind is boggled.
|
||||
@ -260,6 +261,12 @@ class ProcessManager:
|
||||
if process:
|
||||
process.stop()
|
||||
del self.processes[proc_name]
|
||||
|
||||
def restart(self, proc_name: str):
|
||||
"""Destroy a process, then start it again.
|
||||
Useful to skip the repeat_delay timer of infinite processes like Status or Log."""
|
||||
self.kill(proc_name)
|
||||
self.start(proc_name)
|
||||
|
||||
|
||||
# I named this variable with title-case, to indicate that it's a Singleton.
|
||||
|
@ -55,8 +55,9 @@ class SVN_OT_explain_status(Operator):
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def init_svn_of_current_file(_scene=None):
|
||||
"""When opening or saving a .blend file:
|
||||
def ensure_svn_of_current_file(_scene=None):
|
||||
"""When opening or saving a .blend file, it's possible that the new .blend
|
||||
is part of an SVN repository. If this is the case, do the following:
|
||||
- Initialize SVN Scene info
|
||||
- Initialize Repository
|
||||
- Try to authenticate
|
||||
@ -68,6 +69,17 @@ def init_svn_of_current_file(_scene=None):
|
||||
prefs = get_addon_prefs(context)
|
||||
prefs.sync_repo_info_file()
|
||||
|
||||
repo = prefs.active_repo
|
||||
if repo and repo.is_cred_entered and repo.authenticated:
|
||||
status = Processes.get('Status')
|
||||
if status and time.time() - status.timestamp_last_update < status.repeat_delay + 5:
|
||||
# If the SVN Status background process has recently processed data,
|
||||
# there is no need to re-initialize everything.
|
||||
# This happens when a file that was already saved is saved again,
|
||||
# or a file from the same repository as the previous one is loaded.
|
||||
Processes.restart('Status')
|
||||
return
|
||||
|
||||
for repo in prefs.repositories:
|
||||
# This would ideally only run when opening Blender for the first
|
||||
# time, but there is no app handler for that, sadly.
|
||||
@ -338,22 +350,22 @@ def mark_current_file_as_modified(_dummy1=None, _dummy2=None):
|
||||
|
||||
|
||||
def delayed_init_svn(delay=1):
|
||||
bpy.app.timers.register(init_svn_of_current_file, first_interval=delay)
|
||||
bpy.app.timers.register(ensure_svn_of_current_file, first_interval=delay)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.app.handlers.load_post.append(init_svn_of_current_file)
|
||||
bpy.app.handlers.load_post.append(ensure_svn_of_current_file)
|
||||
|
||||
bpy.app.handlers.save_post.append(init_svn_of_current_file)
|
||||
bpy.app.handlers.save_post.append(ensure_svn_of_current_file)
|
||||
bpy.app.handlers.save_post.append(mark_current_file_as_modified)
|
||||
|
||||
delayed_init_svn()
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.app.handlers.load_post.remove(init_svn_of_current_file)
|
||||
bpy.app.handlers.load_post.remove(ensure_svn_of_current_file)
|
||||
|
||||
bpy.app.handlers.save_post.remove(init_svn_of_current_file)
|
||||
bpy.app.handlers.save_post.remove(ensure_svn_of_current_file)
|
||||
bpy.app.handlers.save_post.remove(mark_current_file_as_modified)
|
||||
|
||||
|
||||
|
@ -114,6 +114,7 @@ class SVN_UL_file_list(UIList):
|
||||
explainer.status = status
|
||||
explainer.file_rel_path = file_entry.svn_path
|
||||
|
||||
|
||||
@classmethod
|
||||
def cls_filter_items(cls, context, data, propname):
|
||||
"""By moving all of this logic to a classmethod (and all the filter
|
||||
@ -188,13 +189,6 @@ def draw_repo_file_list(context, layout, repo):
|
||||
return
|
||||
|
||||
main_col = layout.column()
|
||||
main_col.enabled = False
|
||||
status_proc = Processes.get('Status')
|
||||
time_since_last_update = 1000
|
||||
if status_proc:
|
||||
time_since_last_update = time.time() - status_proc.timestamp_last_update
|
||||
if time_since_last_update < 30:
|
||||
main_col.enabled = True
|
||||
main_row = main_col.row()
|
||||
split = main_row.split(factor=0.6)
|
||||
filepath_row = split.row()
|
||||
@ -207,11 +201,6 @@ def draw_repo_file_list(context, layout, repo):
|
||||
ops_row.alignment = 'RIGHT'
|
||||
ops_row.label(text="Operations")
|
||||
|
||||
timer_row = main_row.row()
|
||||
timer_row.alignment = 'RIGHT'
|
||||
timer_row.operator("svn.custom_tooltip", icon='BLANK1', text="",
|
||||
emboss=False).tooltip = "Time since last file status update: " + str(time_since_last_update) + 's'
|
||||
|
||||
row = main_col.row()
|
||||
row.template_list(
|
||||
"SVN_UL_file_list",
|
||||
|
@ -28,9 +28,8 @@ def draw_outdated_file_warning(self, context):
|
||||
row.operator('svn.resolve_conflict',
|
||||
text="SVN: This .blend file is conflicted.", icon='ERROR')
|
||||
elif current_file.repos_status != 'none':
|
||||
warning = row.operator(
|
||||
'svn.custom_tooltip', text="SVN: This .blend file is outdated.", icon='ERROR')
|
||||
warning.tooltip = "The currently opened .blend file has a newer version available on the remote repository. This means any changes in this file will result in a conflict, and potential loss of data. See the SVN panel for info"
|
||||
op = row.operator('svn.revert_and_update_file', text="SVN: This .blend file is outdated.", icon='ERROR')
|
||||
op.file_rel_path = repo.current_blend_file.svn_path
|
||||
|
||||
|
||||
def register():
|
||||
|
Loading…
Reference in New Issue
Block a user