Blender SVN: New Features #273
@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
import bpy
|
||||
from collections import OrderedDict
|
||||
|
||||
SVN_STATUS_DATA = OrderedDict(
|
||||
@ -94,7 +95,6 @@ SVN_STATUS_DATA = OrderedDict(
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# Based on PySVN/svn/constants.py/STATUS_TYPE_LOOKUP.
|
||||
ENUM_SVN_STATUS = [
|
||||
(status, status.title(),
|
||||
@ -119,3 +119,15 @@ SVN_STATUS_CHAR_TO_NAME = {
|
||||
|
||||
SVN_STATUS_NAME_TO_CHAR = {value: key for key,
|
||||
value in SVN_STATUS_CHAR_TO_NAME.items()}
|
||||
|
||||
DEPTH_ENUM = bpy.props.EnumProperty(
|
||||
name="Depth",
|
||||
description="The depth to which this directory should be updated", # For non-directory entries, this property is not used.
|
||||
items=[
|
||||
('INFINITY', 'Recursive (Default)', "Default update behaviour: Updates will recursively pull in any files or subdirectories not already present", 'OUTLINER', 3),
|
||||
('IMMEDIATES', 'Non-Recursive', "Updates will pull in any files or subdirectories not already present; those subdirectories' will be left empty", 'PRESET', 2),
|
||||
('FILES', 'Files Only', "Updates will pull in any files not already present, but not subdirectories", 'FILE', 1),
|
||||
('EMPTY', 'Empty', "Updates will not pull in any files or subdirectories not already present", 'SELECT_SET', 0),
|
||||
],
|
||||
default='INFINITY'
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ from send2trash import send2trash
|
||||
from ..threaded.execute_subprocess import execute_svn_command
|
||||
from ..threaded.background_process import Processes
|
||||
from ..util import get_addon_prefs, redraw_viewport
|
||||
from ..constants import DEPTH_ENUM
|
||||
|
||||
|
||||
class SVN_Operator:
|
||||
@ -456,42 +457,49 @@ class SVN_OT_resolve_conflict(May_Modifiy_Current_Blend, Operator):
|
||||
file_entry.status = 'normal'
|
||||
|
||||
|
||||
class SVN_OT_set_directory_depth(Operator):
|
||||
class SVN_OT_set_directory_depth(SVN_Operator_Single_File, Operator):
|
||||
bl_idname = "svn.set_directory_depth"
|
||||
bl_label = "Set Directory Depth"
|
||||
bl_description = "Set update depth of this directory"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
depth: EnumProperty(
|
||||
name="Depth",
|
||||
description="The depth to which this directory should be updated", # For non-directory entries, this property is not used.
|
||||
items=[
|
||||
('INFINITY', 'Recursive (Default)', "Default update behaviour: Updates will recursively pull in any files or subdirectories not already present", 'OUTLINER', 3),
|
||||
('IMMEDIATES', 'Non-Recursive', "Updates will pull in any files or subdirectories not already present; those subdirectories' will be left empty", 'PRESET', 2),
|
||||
('FILES', 'Files Only', "Updates will pull in any files not already present, but not subdirectories", 'FILE', 1),
|
||||
('EMPTY', 'Empty', "Updates will not pull in any files or subdirectories not already present", 'SELECT_SET', 0),
|
||||
],
|
||||
default='INFINITY'
|
||||
depth: DEPTH_ENUM
|
||||
popup: BoolProperty(
|
||||
name="Popup",
|
||||
description="Whether the operator should use a pop-up prompt",
|
||||
default=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
depth = cls.__annotations__['depth']
|
||||
desc = depth.keywords['description']
|
||||
items = depth.keywords['items']
|
||||
desc = DEPTH_ENUM.keywords['description']
|
||||
items = DEPTH_ENUM.keywords['items']
|
||||
for identifier, name, item_desc, icon, num in items:
|
||||
if identifier == properties.depth:
|
||||
return desc + ":\n" + item_desc
|
||||
|
||||
def execute(self, context):
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
active_file = repo.active_file
|
||||
def invoke(self, context, event):
|
||||
if self.popup:
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
return self.execute(context)
|
||||
|
||||
if not active_file.is_dir:
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split=True
|
||||
layout.use_property_decorate=False
|
||||
layout.prop(self, 'depth')
|
||||
|
||||
def execute(self, context):
|
||||
file_entry = self.get_file(context)
|
||||
|
||||
if not file_entry.is_dir:
|
||||
self.report({'ERROR'}, "Active file entry is not a directory")
|
||||
return {'CANCELLED'}
|
||||
|
||||
active_file.depth = self.depth
|
||||
file_entry.depth = self.depth
|
||||
|
||||
# Depth info needs to be saved to file to make sure it doesn't get lost.
|
||||
get_addon_prefs(context).save_repo_info_to_file()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -509,8 +517,11 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
prefs = get_addon_prefs(context)
|
||||
|
||||
repo.external_files.clear()
|
||||
# Load folder depth data from file.
|
||||
prefs.load_repo_info_from_file()
|
||||
self.execute_svn_command(context, ["svn", "cleanup"])
|
||||
repo.reload_svn_log(context)
|
||||
|
||||
|
@ -129,11 +129,13 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
saved_props = {'url', 'directory', 'name',
|
||||
'username', 'password', 'display_name'}
|
||||
repo_data = {}
|
||||
for repo in self['repositories']:
|
||||
directory = repo.get('directory', '')
|
||||
for repo_dict, repo in zip(self['repositories'], self.repositories):
|
||||
directory = repo_dict.get('directory', '')
|
||||
|
||||
repo_data[directory] = {
|
||||
key: value for key, value in repo.to_dict().items() if key in saved_props}
|
||||
key: value for key, value in repo_dict.to_dict().items() if key in saved_props}
|
||||
|
||||
repo_data[directory]['custom_directory_depths'] = {dir_entry.svn_path: dir_entry.depth for dir_entry in repo.external_files if dir_entry.is_dir and dir_entry.depth != 'INFINITY'}
|
||||
|
||||
filepath = Path(bpy.utils.user_resource('CONFIG')) / \
|
||||
Path("blender_svn.txt")
|
||||
@ -157,7 +159,15 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
repo = self.repositories.add()
|
||||
repo.directory = directory
|
||||
for key, value in repo_data.items():
|
||||
setattr(repo, key, value)
|
||||
if key == 'custom_directory_depths':
|
||||
for dir_path, depth in repo_data['custom_directory_depths'].items():
|
||||
dir_entry = self.external_files.get(dir_path)
|
||||
if not dir_entry:
|
||||
dir_entry = self.external_files.add()
|
||||
dir_entry.svn_path = dir_path
|
||||
dir_entry.depth = depth
|
||||
else:
|
||||
setattr(repo, key, value)
|
||||
finally:
|
||||
self.loading = False
|
||||
|
||||
|
@ -48,17 +48,13 @@ class SVN_file(PropertyGroup):
|
||||
options=set()
|
||||
)
|
||||
|
||||
depth: EnumProperty(
|
||||
name="Depth",
|
||||
description="The depth to which this directory should be updated. If this is a file and not a directory, this value isn't used",
|
||||
items=[
|
||||
('EMPTY', 'empty', "Updates will not pull in any files or subdirectories not already present"),
|
||||
('FILES', 'files', "Updates will pull in any files not already present, but not subdirectories"),
|
||||
('IMMEDIATES', 'immediates', "Updates will pull in any files or subdirectories not already present; those subdirectories' will have depth=empty"),
|
||||
('INFINITY', 'infinity', "Default update behaviour: Updates will pull in any files or subdirectories not already present; those subdirectories' will have depth-infinity"),
|
||||
],
|
||||
default='INFINITY'
|
||||
)
|
||||
depth: constants.DEPTH_ENUM
|
||||
@property
|
||||
def depth_icon(self):
|
||||
for identifier, name, item_desc, icon, num in constants.DEPTH_ENUM.keywords['items']:
|
||||
if identifier == self.depth:
|
||||
return icon
|
||||
|
||||
status: EnumProperty(
|
||||
name="Status",
|
||||
description="SVN Status of the file in the local repository (aka working copy)",
|
||||
@ -269,6 +265,7 @@ class SVN_repository(PropertyGroup):
|
||||
url: StringProperty(
|
||||
name="URL",
|
||||
description="URL of the remote repository",
|
||||
update=update_repo_info_file
|
||||
)
|
||||
|
||||
def update_directory(self, context):
|
||||
|
@ -153,16 +153,16 @@ class BGP_SVN_Status(BackgroundProcess):
|
||||
# NOTE: This one-by-one updating functionality conflicts with a potential
|
||||
# future support for partial check-outs, so that would require storing user-intended
|
||||
# partially checked out folders separately somewhere.
|
||||
self.list_command_output = execute_svn_command(
|
||||
context,
|
||||
["svn", "list", "--recursive", "--xml"],
|
||||
use_cred=True,
|
||||
)
|
||||
# self.list_command_output = execute_svn_command(
|
||||
# context,
|
||||
# ["svn", "list", "--recursive", "--xml"],
|
||||
# use_cred=True,
|
||||
# )
|
||||
|
||||
def process_output(self, context, prefs):
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
update_file_list_svn_status(context, svn_status_xml_to_dict(self.output))
|
||||
update_file_list_svn_list(context, self.list_command_output)
|
||||
# update_file_list_svn_list(context, self.list_command_output)
|
||||
repo.refresh_ui_lists(context)
|
||||
self.timestamp_last_update = time.time()
|
||||
if prefs.do_auto_updates:
|
||||
|
@ -74,8 +74,9 @@ class SVN_UL_file_list(UIList):
|
||||
# I think it's better to let the user know in advance.
|
||||
statuses.append('conflicted')
|
||||
# Updating the file will create an actual conflict.
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
if not prefs.do_auto_updates:
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
|
||||
elif file_entry.status == 'conflicted':
|
||||
ops.append(ops_ui.operator('svn.resolve_conflict',
|
||||
@ -88,19 +89,26 @@ class SVN_UL_file_list(UIList):
|
||||
# From user POV it makes a bit more sense to call a file that doesn't
|
||||
# exist yet "added" instead of "outdated".
|
||||
statuses.append('added')
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
if not prefs.do_auto_updates:
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
elif file_entry.status == 'normal' and file_entry.repos_status == 'modified':
|
||||
# From user POV, this file is outdated, not 'normal'.
|
||||
statuses = ['none']
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
if not prefs.do_auto_updates:
|
||||
ops.append(ops_ui.operator(
|
||||
'svn.update_single', text="", icon='IMPORT'))
|
||||
elif file_entry.status in ['normal', 'external', 'ignored']:
|
||||
pass
|
||||
else:
|
||||
print("Unknown file status: ", file_entry.svn_path,
|
||||
file_entry.status, file_entry.repos_status)
|
||||
|
||||
if file_entry.is_dir:
|
||||
op = ops_ui.operator('svn.set_directory_depth', text="", icon=file_entry.depth_icon)
|
||||
op.popup=True
|
||||
ops.append(op)
|
||||
|
||||
for op in ops:
|
||||
if hasattr(op, 'file_rel_path'):
|
||||
op.file_rel_path = file_entry.svn_path
|
||||
|
Loading…
Reference in New Issue
Block a user