SVN: Checkout, Multi-Repo, Optimizations & Clean-up #104
@ -2,6 +2,17 @@
|
||||
# (c) 2021, Blender Foundation - Paul Golter
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
from . import (
|
||||
props,
|
||||
repository,
|
||||
operators,
|
||||
threaded,
|
||||
ui,
|
||||
prefs,
|
||||
svn_info,
|
||||
)
|
||||
import importlib
|
||||
from bpy.utils import register_class, unregister_class
|
||||
bl_info = {
|
||||
"name": "Blender SVN",
|
||||
"author": "Demeter Dzadik, Paul Golter",
|
||||
@ -15,18 +26,6 @@ bl_info = {
|
||||
"category": "Generic",
|
||||
}
|
||||
|
||||
from bpy.utils import register_class, unregister_class
|
||||
import importlib
|
||||
|
||||
from . import (
|
||||
props,
|
||||
repository,
|
||||
operators,
|
||||
threaded,
|
||||
ui,
|
||||
prefs,
|
||||
svn_info,
|
||||
)
|
||||
|
||||
modules = [
|
||||
props,
|
||||
@ -38,6 +37,7 @@ modules = [
|
||||
svn_info,
|
||||
]
|
||||
|
||||
|
||||
def register_unregister_modules(modules: list, register: bool):
|
||||
"""Recursively register or unregister modules by looking for either
|
||||
un/register() functions or lists named `registry` which should be a list of
|
||||
@ -54,7 +54,8 @@ def register_unregister_modules(modules: list, register: bool):
|
||||
register_func(c)
|
||||
except Exception as e:
|
||||
un = 'un' if not register else ''
|
||||
print(f"Warning: SVN failed to {un}register class: {c.__name__}")
|
||||
print(
|
||||
f"Warning: SVN failed to {un}register class: {c.__name__}")
|
||||
print(e)
|
||||
|
||||
if hasattr(m, 'modules'):
|
||||
@ -65,8 +66,10 @@ def register_unregister_modules(modules: list, register: bool):
|
||||
elif hasattr(m, 'unregister'):
|
||||
m.unregister()
|
||||
|
||||
|
||||
def register():
|
||||
register_unregister_modules(modules, True)
|
||||
|
||||
|
||||
def unregister():
|
||||
register_unregister_modules(modules, False)
|
||||
|
@ -41,7 +41,8 @@ class SVN_Operator_Single_File(SVN_Operator):
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
if not self.file_exists(context) and not type(self).missing_file_allowed:
|
||||
# If the operator requires the file to exist and it doesn't, cancel.
|
||||
self.report({'ERROR'}, f'File is no longer on the file system: "{self.file_rel_path}"')
|
||||
self.report(
|
||||
{'ERROR'}, f'File is no longer on the file system: "{self.file_rel_path}"')
|
||||
return {'CANCELLED'}
|
||||
|
||||
status = Processes.get('Status')
|
||||
@ -145,7 +146,8 @@ class SVN_OT_update_single(May_Modifiy_Current_Blend, Operator):
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.will_conflict = False
|
||||
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(self.file_rel_path)
|
||||
file_entry = context.scene.svn.get_repo(
|
||||
context).get_file_by_svn_path(self.file_rel_path)
|
||||
if file_entry.status not in ['normal', 'none']:
|
||||
self.will_conflict = True
|
||||
|
||||
@ -179,7 +181,8 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
||||
revision: IntProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(self.file_rel_path)
|
||||
file_entry = context.scene.svn.get_repo(
|
||||
context).get_file_by_svn_path(self.file_rel_path)
|
||||
if self.file_is_current_blend(context) and file_entry.status != 'normal':
|
||||
self.report({'ERROR'},
|
||||
'You must first revert or commit the changes to this file.')
|
||||
@ -187,7 +190,8 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
||||
return super().invoke(context, event)
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(self.file_rel_path)
|
||||
file_entry = context.scene.svn.get_repo(
|
||||
context).get_file_by_svn_path(self.file_rel_path)
|
||||
if file_entry.status == 'modified':
|
||||
# If file has local modifications, let's avoid a conflict by cancelling
|
||||
# and telling the user to resolve it in advance.
|
||||
@ -197,7 +201,8 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
||||
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
["svn", "up" ,f"-r{self.revision}", f"{self.file_rel_path}", "--accept", "postpone"],
|
||||
["svn", "up", f"-r{self.revision}",
|
||||
f"{self.file_rel_path}", "--accept", "postpone"],
|
||||
use_cred=True
|
||||
)
|
||||
|
||||
@ -227,7 +232,7 @@ class SVN_OT_restore_file(May_Modifiy_Current_Blend, Operator):
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
context,
|
||||
["svn", "revert", f"{self.file_rel_path}"]
|
||||
)
|
||||
|
||||
@ -327,7 +332,7 @@ class SVN_OT_remove_file(SVN_Operator_Single_File, Warning_Operator, Operator):
|
||||
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
context,
|
||||
["svn", "remove", f"{self.file_rel_path}"]
|
||||
)
|
||||
|
||||
@ -375,7 +380,8 @@ class SVN_OT_resolve_conflict(May_Modifiy_Current_Blend, Operator):
|
||||
def _execute(self, context: Context) -> Set[str]:
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
["svn", "resolve", f"{self.file_rel_path}", "--accept", f"{self.resolve_method}"]
|
||||
["svn", "resolve", f"{self.file_rel_path}",
|
||||
"--accept", f"{self.resolve_method}"]
|
||||
)
|
||||
|
||||
return {"FINISHED"}
|
||||
@ -400,7 +406,7 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
|
||||
|
||||
repo.external_files.clear()
|
||||
self.execute_svn_command(context, ["svn", "cleanup"])
|
||||
repo.reload_svn_log(context)
|
||||
|
@ -13,6 +13,7 @@ from ..threaded.background_process import Processes
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class SVN_OT_checkout_initiate(Operator):
|
||||
bl_idname = "svn.checkout_initiate"
|
||||
bl_label = "Initiate SVN Checkout"
|
||||
@ -20,9 +21,9 @@ class SVN_OT_checkout_initiate(Operator):
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
create: BoolProperty(
|
||||
name = "Create Repo Entry",
|
||||
description = "Whether a new repo entry should be created, or the active one used",
|
||||
default = True
|
||||
name="Create Repo Entry",
|
||||
description="Whether a new repo entry should be created, or the active one used",
|
||||
default=True
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
@ -35,6 +36,7 @@ class SVN_OT_checkout_initiate(Operator):
|
||||
prefs.checkout_mode = True
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SVN_OT_checkout_finalize(Operator, SVN_Operator):
|
||||
bl_idname = "svn.checkout_finalize"
|
||||
bl_label = "Finalize SVN Checkout"
|
||||
@ -54,11 +56,12 @@ class SVN_OT_checkout_finalize(Operator, SVN_Operator):
|
||||
['svn', 'cleanup']
|
||||
)
|
||||
p = subprocess.Popen(
|
||||
["svn", "checkout", f"--username={repo.username}", f"--password={repo.password}", repo.url, repo.display_name],
|
||||
shell = False,
|
||||
cwd = repo.directory+"/",
|
||||
stdout = subprocess.PIPE,
|
||||
start_new_session = True
|
||||
["svn", "checkout", f"--username={repo.username}",
|
||||
f"--password={repo.password}", repo.url, repo.display_name],
|
||||
shell=False,
|
||||
cwd=repo.directory+"/",
|
||||
stdout=subprocess.PIPE,
|
||||
start_new_session=True
|
||||
)
|
||||
repo.directory = str((Path(repo.directory) / repo.display_name))
|
||||
while True:
|
||||
@ -87,6 +90,7 @@ class SVN_OT_checkout_cancel(Operator):
|
||||
prefs.repositories.remove(prefs.active_repo_idx)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
registry = [
|
||||
SVN_OT_checkout_initiate,
|
||||
SVN_OT_checkout_finalize,
|
||||
|
@ -86,7 +86,7 @@ class SVN_OT_commit(SVN_Operator, Popup_Operator, Operator):
|
||||
self.is_file_really_dirty = bpy.data.is_dirty
|
||||
|
||||
# This flag is needed as a workaround because bpy.data.is_dirty gets set to True
|
||||
# when we change the operator's checkboxes or
|
||||
# when we change the operator's checkboxes or
|
||||
self.is_file_dirty_on_invoke = bpy.data.is_dirty
|
||||
|
||||
for f in repo.external_files:
|
||||
@ -119,7 +119,8 @@ class SVN_OT_commit(SVN_Operator, Popup_Operator, Operator):
|
||||
icon = 'ERROR'
|
||||
op_row = split.row()
|
||||
op_row.alignment = 'LEFT'
|
||||
op_row.operator('svn.save_during_commit', icon='FILE_BLEND', text="Save")
|
||||
op_row.operator('svn.save_during_commit',
|
||||
icon='FILE_BLEND', text="Save")
|
||||
row.label(text=text, icon=icon)
|
||||
|
||||
row = layout.row()
|
||||
@ -159,9 +160,9 @@ class SVN_OT_commit(SVN_Operator, Popup_Operator, Operator):
|
||||
self.set_predicted_file_statuses(files_to_commit)
|
||||
Processes.stop('Status')
|
||||
Processes.start('Commit',
|
||||
commit_msg=repo.commit_message,
|
||||
file_list=filepaths
|
||||
)
|
||||
commit_msg=repo.commit_message,
|
||||
file_list=filepaths
|
||||
)
|
||||
|
||||
report = f"{(len(files_to_commit))} files"
|
||||
if len(files_to_commit) == 1:
|
||||
|
@ -19,9 +19,9 @@ class SVN_OT_update_all(May_Modifiy_Current_Blend, Operator):
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
revision: IntProperty(
|
||||
name = "Revision",
|
||||
description = "Which revision to revert the repository to. 0 means to update to the latest version instead",
|
||||
default = 0
|
||||
name="Revision",
|
||||
description="Which revision to revert the repository to. 0 means to update to the latest version instead",
|
||||
default=0
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -50,7 +50,7 @@ class SVN_OT_update_all(May_Modifiy_Current_Blend, Operator):
|
||||
for f in repo.external_files:
|
||||
if f.status in ['modified', 'added', 'conflicted', 'deleted', 'missing', 'unversioned']:
|
||||
return context.window_manager.invoke_props_dialog(self, width=500)
|
||||
|
||||
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
@ -58,10 +58,13 @@ class SVN_OT_update_all(May_Modifiy_Current_Blend, Operator):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.label(text="You have uncommitted local changes.")
|
||||
col.label(text="These won't be lost, but if you want to revert the state of the entire local repository to a ")
|
||||
col.label(text="past point in time, you would get a better result if you reverted or committed your changes first.")
|
||||
col.label(
|
||||
text="These won't be lost, but if you want to revert the state of the entire local repository to a ")
|
||||
col.label(
|
||||
text="past point in time, you would get a better result if you reverted or committed your changes first.")
|
||||
col.separator()
|
||||
col.label(text="Press OK to proceed anyways. Click out of this window to cancel.")
|
||||
col.label(
|
||||
text="Press OK to proceed anyways. Click out of this window to cancel.")
|
||||
super().draw(context)
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
@ -72,7 +75,7 @@ class SVN_OT_update_all(May_Modifiy_Current_Blend, Operator):
|
||||
if self.revision > 0:
|
||||
command.insert(2, f"-r{self.revision}")
|
||||
self.execute_svn_command(
|
||||
context,
|
||||
context,
|
||||
command,
|
||||
use_cred=True
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ from bpy.props import BoolProperty, StringProperty
|
||||
from bpy.types import Operator
|
||||
from ..threaded.background_process import Processes
|
||||
|
||||
|
||||
class SVN_OT_custom_tooltip(Operator):
|
||||
"""Tooltip"""
|
||||
bl_idname = "svn.custom_tooltip"
|
||||
@ -70,4 +71,4 @@ class SVN_OT_clear_error(Operator):
|
||||
registry = [
|
||||
SVN_OT_custom_tooltip,
|
||||
SVN_OT_clear_error
|
||||
]
|
||||
]
|
||||
|
@ -15,6 +15,7 @@ import json
|
||||
from pathlib import Path
|
||||
from .threaded.background_process import Processes
|
||||
|
||||
|
||||
class SVN_addon_preferences(AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
@ -56,7 +57,7 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
self.active_repo_idx = scene_svn_idx
|
||||
self.idx_updating = False
|
||||
return
|
||||
|
||||
|
||||
if not active_repo.authenticated and not active_repo.auth_failed and active_repo.is_cred_entered:
|
||||
active_repo.authenticate(context)
|
||||
|
||||
@ -75,14 +76,15 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
)
|
||||
|
||||
active_repo_mode: EnumProperty(
|
||||
name = "Choose Repository",
|
||||
description = "Whether the add-on should communicate with the repository of the currently opened .blend file, or the repository selected in the list below",
|
||||
items = [
|
||||
name="Choose Repository",
|
||||
description="Whether the add-on should communicate with the repository of the currently opened .blend file, or the repository selected in the list below",
|
||||
items=[
|
||||
('CURRENT_BLEND', "Current Blend", "Check if the current .blend file is in an SVN repository, and communicate with that if that is the case. The file list will display only the files of the repository of the current .blend file. If the current .blend is not in a repository, do nothing"),
|
||||
('SELECTED_REPO', "Selected Repo", "Communicate with the selected repository")
|
||||
('SELECTED_REPO', "Selected Repo",
|
||||
"Communicate with the selected repository")
|
||||
],
|
||||
default = 'CURRENT_BLEND',
|
||||
update = update_active_repo_mode
|
||||
default='CURRENT_BLEND',
|
||||
update=update_active_repo_mode
|
||||
)
|
||||
|
||||
active_repo_idx: IntProperty(
|
||||
@ -101,11 +103,11 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
return self.repositories[self.active_repo_idx]
|
||||
|
||||
debug_mode: BoolProperty(
|
||||
name = "Debug Mode",
|
||||
description = "Enable some debug UI",
|
||||
default = False
|
||||
name="Debug Mode",
|
||||
description="Enable some debug UI",
|
||||
default=False
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def is_busy(self):
|
||||
return Processes.is_running('Commit', 'Update')
|
||||
@ -117,21 +119,25 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
)
|
||||
|
||||
def save_repo_info_to_file(self):
|
||||
saved_props = {'url', 'directory', 'name', 'username', 'password', 'display_name'}
|
||||
saved_props = {'url', 'directory', 'name',
|
||||
'username', 'password', 'display_name'}
|
||||
repo_data = {}
|
||||
for repo in self['repositories']:
|
||||
directory = repo.get('directory', '')
|
||||
|
||||
repo_data[directory] = {key:value for key, value in repo.to_dict().items() if key in saved_props}
|
||||
repo_data[directory] = {
|
||||
key: value for key, value in repo.to_dict().items() if key in saved_props}
|
||||
|
||||
filepath = Path(bpy.utils.user_resource('CONFIG')) / Path("blender_svn.txt")
|
||||
filepath = Path(bpy.utils.user_resource('CONFIG')) / \
|
||||
Path("blender_svn.txt")
|
||||
with open(filepath, "w") as f:
|
||||
json.dump(repo_data, f, indent=4)
|
||||
|
||||
def load_repo_info_from_file(self):
|
||||
self.loading = True
|
||||
try:
|
||||
filepath = Path(bpy.utils.user_resource('CONFIG')) / Path("blender_svn.txt")
|
||||
filepath = Path(bpy.utils.user_resource(
|
||||
'CONFIG')) / Path("blender_svn.txt")
|
||||
if not filepath.exists():
|
||||
return
|
||||
|
||||
@ -154,6 +160,7 @@ class SVN_addon_preferences(AddonPreferences):
|
||||
|
||||
draw = ui_prefs.draw_prefs
|
||||
|
||||
|
||||
registry = [
|
||||
SVN_addon_preferences
|
||||
]
|
||||
|
@ -2,18 +2,16 @@
|
||||
# (c) 2021, Blender Foundation - Paul Golter
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
from .util import get_addon_prefs
|
||||
from bpy.props import StringProperty, PointerProperty
|
||||
from bpy.types import PropertyGroup
|
||||
import bpy
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, List, Tuple, Set
|
||||
from . import wheels
|
||||
# This will load the dateutil and BAT wheel files.
|
||||
wheels.preload_dependencies()
|
||||
|
||||
from typing import Optional, Dict, Any, List, Tuple, Set
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import StringProperty, PointerProperty
|
||||
|
||||
from .util import get_addon_prefs
|
||||
|
||||
class SVN_scene_properties(PropertyGroup):
|
||||
"""Subversion properties to match this scene to a repo in the UserPrefs"""
|
||||
|
@ -14,6 +14,7 @@ from .svn_info import get_svn_info
|
||||
from .util import get_addon_prefs
|
||||
from . import constants
|
||||
|
||||
|
||||
class SVN_file(PropertyGroup):
|
||||
"""Property Group that can represent a version of a File in an SVN repository."""
|
||||
|
||||
@ -52,7 +53,8 @@ class SVN_file(PropertyGroup):
|
||||
items=[
|
||||
("NONE", "None", "File status is not predicted, but actual."),
|
||||
("SVN_UP", "Update", "File status is predicted by `svn up`. Status is protected until process is finished."),
|
||||
("SVN_COMMIT", "Commit", "File status is predicted by `svn commit`. Status is protected until process is finished."),
|
||||
("SVN_COMMIT", "Commit",
|
||||
"File status is predicted by `svn commit`. Status is protected until process is finished."),
|
||||
("SKIP_ONCE", "Skip Once", "File status is predicted by a working-copy svn file operation, like Revert. Next status update should be ignored, and this enum should be set to SKIPPED_ONCE."),
|
||||
("SKIPPED_ONCE", "Skipped Once", "File status update was skipped. Next status update can be considered accurate, and this flag can be reset to NONE. Until then, operations on this file should remain disabled."),
|
||||
],
|
||||
@ -164,6 +166,7 @@ class SVN_log(PropertyGroup):
|
||||
name="Changed Files",
|
||||
description="List of file entries that were affected by this revision"
|
||||
)
|
||||
|
||||
def changes_file(self, file: SVN_file) -> bool:
|
||||
for affected_file in self.changed_files:
|
||||
if affected_file.svn_path == "/"+file.svn_path:
|
||||
@ -190,7 +193,7 @@ class SVN_log(PropertyGroup):
|
||||
rev = "r"+str(self.revision_number)
|
||||
auth = self.revision_author
|
||||
files = " ".join([f.svn_path for f in self.changed_files])
|
||||
msg = self.commit_message
|
||||
msg = self.commit_message
|
||||
date = self.revision_date_simple
|
||||
return " ".join([rev, auth, files, msg, date]).lower()
|
||||
|
||||
@ -200,6 +203,7 @@ class SVN_log(PropertyGroup):
|
||||
default=False
|
||||
)
|
||||
|
||||
|
||||
class SVN_repository(PropertyGroup):
|
||||
### Basic SVN Info. ###
|
||||
@property
|
||||
@ -210,14 +214,14 @@ class SVN_repository(PropertyGroup):
|
||||
get_addon_prefs(context).save_repo_info_to_file()
|
||||
|
||||
display_name: StringProperty(
|
||||
name = "Display Name",
|
||||
description = "Display name of this SVN repository",
|
||||
update = update_repo_info_file
|
||||
name="Display Name",
|
||||
description="Display name of this SVN repository",
|
||||
update=update_repo_info_file
|
||||
)
|
||||
|
||||
url: StringProperty(
|
||||
name = "URL",
|
||||
description = "URL of the remote repository",
|
||||
name="URL",
|
||||
description="URL of the remote repository",
|
||||
)
|
||||
|
||||
def update_directory(self, context):
|
||||
@ -245,8 +249,8 @@ class SVN_repository(PropertyGroup):
|
||||
dir_path = Path(self.directory)
|
||||
root_dir, base_url = get_svn_info(self.directory)
|
||||
return (
|
||||
dir_path.exists() and
|
||||
dir_path.is_dir() and
|
||||
dir_path.exists() and
|
||||
dir_path.is_dir() and
|
||||
root_dir and base_url and
|
||||
root_dir == self.directory and
|
||||
base_url == self.url
|
||||
@ -385,7 +389,6 @@ class SVN_repository(PropertyGroup):
|
||||
current = file.revision
|
||||
return latest > current
|
||||
|
||||
|
||||
### SVN File List. ###
|
||||
external_files: CollectionProperty(type=SVN_file)
|
||||
|
||||
@ -461,8 +464,9 @@ class SVN_repository(PropertyGroup):
|
||||
|
||||
# Filter out log entries that did not affect the selected file.
|
||||
self.log.foreach_set(
|
||||
'affects_active_file',
|
||||
[log_entry.changes_file(self.active_file) for log_entry in self.log]
|
||||
'affects_active_file',
|
||||
[log_entry.changes_file(self.active_file)
|
||||
for log_entry in self.log]
|
||||
)
|
||||
|
||||
external_files_active_index: IntProperty(
|
||||
@ -509,8 +513,8 @@ class SVN_repository(PropertyGroup):
|
||||
return self.get_file_by_absolute_path(bpy.data.filepath)
|
||||
|
||||
### File List UIList filter properties ###
|
||||
# Filtering properties are normally stored on the UIList,
|
||||
# but then they cannot be accessed from anywhere else,
|
||||
# Filtering properties are normally stored on the UIList,
|
||||
# but then they cannot be accessed from anywhere else,
|
||||
# since template_list() does not return the UIList instance.
|
||||
# We need to be able to access them outside of drawing code, to be able to
|
||||
# ensure that a filtered out entry can never be the active one.
|
||||
@ -531,13 +535,13 @@ class SVN_repository(PropertyGroup):
|
||||
|
||||
def update_file_filter(self, context):
|
||||
"""Should run when any of the SVN file list search filters are changed."""
|
||||
|
||||
|
||||
UI_LIST = bpy.types.UI_UL_list
|
||||
if self.file_search_filter:
|
||||
filter_list = UI_LIST.filter_items_by_name(
|
||||
self.file_search_filter,
|
||||
1,
|
||||
self.external_files,
|
||||
self.file_search_filter,
|
||||
1,
|
||||
self.external_files,
|
||||
"name",
|
||||
reverse=False
|
||||
)
|
||||
@ -564,4 +568,4 @@ registry = [
|
||||
SVN_file,
|
||||
SVN_log,
|
||||
SVN_repository,
|
||||
]
|
||||
]
|
||||
|
@ -4,6 +4,7 @@ import subprocess
|
||||
|
||||
from .threaded.execute_subprocess import execute_command
|
||||
|
||||
|
||||
def get_svn_info(path: Path or str) -> Tuple[str, str]:
|
||||
"""Use the `svn info` command to get the root dir, the URL, and the relative URL."""
|
||||
path = Path(path)
|
||||
|
@ -2,7 +2,8 @@
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
import bpy
|
||||
import threading, subprocess
|
||||
import threading
|
||||
import subprocess
|
||||
import random
|
||||
from typing import List
|
||||
|
||||
@ -180,8 +181,8 @@ class BackgroundProcess:
|
||||
if not bpy.app.timers.is_registered(self.timer_function):
|
||||
self.debug_print("Register timer")
|
||||
bpy.app.timers.register(
|
||||
self.timer_function,
|
||||
first_interval=self.first_interval,
|
||||
self.timer_function,
|
||||
first_interval=self.first_interval,
|
||||
persistent=persistent
|
||||
)
|
||||
|
||||
@ -206,6 +207,8 @@ def get_recursive_subclasses(typ) -> List[type]:
|
||||
|
||||
|
||||
processes = {}
|
||||
|
||||
|
||||
class ProcessManager:
|
||||
@property
|
||||
def processes(self):
|
||||
@ -227,7 +230,6 @@ class ProcessManager:
|
||||
if proc_name in self.processes:
|
||||
return self.processes[proc_name].is_running
|
||||
|
||||
|
||||
def get(self, proc_name: str):
|
||||
return self.processes.get(proc_name)
|
||||
|
||||
@ -259,6 +261,7 @@ class ProcessManager:
|
||||
process.stop()
|
||||
del self.processes[proc_name]
|
||||
|
||||
|
||||
# I named this variable with title-case, to indicate that it's a Singleton.
|
||||
# There should only be one.
|
||||
Processes = ProcessManager()
|
||||
Processes = ProcessManager()
|
||||
|
@ -10,6 +10,7 @@ from .background_process import Processes, BackgroundProcess
|
||||
from .execute_subprocess import execute_svn_command
|
||||
from ..util import get_addon_prefs
|
||||
|
||||
|
||||
class BGP_SVN_Commit(BackgroundProcess):
|
||||
name = "Commit"
|
||||
needs_authentication = True
|
||||
@ -33,7 +34,8 @@ class BGP_SVN_Commit(BackgroundProcess):
|
||||
|
||||
Processes.kill('Status')
|
||||
sanitized_commit_msg = self.commit_msg.replace('"', "'")
|
||||
command = ["svn", "commit", "-m", f"{sanitized_commit_msg}"] + self.file_list
|
||||
command = ["svn", "commit", "-m",
|
||||
f"{sanitized_commit_msg}"] + self.file_list
|
||||
self.output = execute_svn_command(
|
||||
context,
|
||||
command,
|
||||
@ -67,4 +69,3 @@ class BGP_SVN_Commit(BackgroundProcess):
|
||||
|
||||
def stop(self):
|
||||
super().stop()
|
||||
|
@ -4,6 +4,7 @@
|
||||
import subprocess
|
||||
from typing import List
|
||||
|
||||
|
||||
def get_credential_commands(context) -> List[str]:
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
assert (repo.is_cred_entered), "No username or password entered for this repository. The UI shouldn't have allowed you to get into a state where you can press an SVN operation button without having your credentials entered, so this is a bug!"
|
||||
|
@ -1,5 +1,6 @@
|
||||
from .background_process import BackgroundProcess
|
||||
|
||||
|
||||
class BGP_SVN_Activate_File(BackgroundProcess):
|
||||
"""This crazy hacky method of activating the file with some delay is necessary
|
||||
because Blender won't let us select the file immediately when changing the
|
||||
|
@ -1,6 +1,7 @@
|
||||
from .background_process import BackgroundProcess, Processes
|
||||
from ..util import redraw_viewport
|
||||
|
||||
|
||||
class BGP_SVN_Redraw_Viewport(BackgroundProcess):
|
||||
name = "Redraw Viewport"
|
||||
repeat_delay = 1
|
||||
@ -18,4 +19,4 @@ class BGP_SVN_Redraw_Viewport(BackgroundProcess):
|
||||
|
||||
|
||||
def register():
|
||||
Processes.start("Redraw Viewport")
|
||||
Processes.start("Redraw Viewport")
|
||||
|
@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
@ -57,7 +58,8 @@ def reload_svn_log(self, context):
|
||||
log_entry.revision_author = r_author
|
||||
|
||||
log_entry.revision_date = r_date
|
||||
log_entry.revision_date_simple = svn_date_simple(r_date).split(" ")[0][5:]
|
||||
log_entry.revision_date_simple = svn_date_simple(r_date).split(" ")[
|
||||
0][5:]
|
||||
|
||||
# File change set is on line 3 until the commit message begins...
|
||||
file_change_lines = chunk[2:-(r_msg_length+1)]
|
||||
@ -74,7 +76,8 @@ def reload_svn_log(self, context):
|
||||
log_file_entry = log_entry.changed_files.add()
|
||||
log_file_entry.name = file_path.name
|
||||
log_file_entry.svn_path = str(file_path.as_posix())
|
||||
log_file_entry.absolute_path = str(repo.svn_to_absolute_path(file_path).as_posix())
|
||||
log_file_entry.absolute_path = str(
|
||||
repo.svn_to_absolute_path(file_path).as_posix())
|
||||
log_file_entry.revision = r_number
|
||||
log_file_entry.status = constants.SVN_STATUS_CHAR_TO_NAME[status_char]
|
||||
|
||||
@ -146,7 +149,8 @@ class BGP_SVN_Log(BackgroundProcess):
|
||||
try:
|
||||
self.output = execute_svn_command(
|
||||
context,
|
||||
["svn", "log", "--verbose", f"-r{latest_log_rev+1}:HEAD", "--limit", "10"],
|
||||
["svn", "log", "--verbose",
|
||||
f"-r{latest_log_rev+1}:HEAD", "--limit", "10"],
|
||||
print_errors=False,
|
||||
use_cred=True
|
||||
)
|
||||
@ -170,7 +174,7 @@ class BGP_SVN_Log(BackgroundProcess):
|
||||
rev_no = repo.log[-1].revision_number
|
||||
return f"Updating log. Current: r{rev_no}..."
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
def svn_date_to_datetime(datetime_str: str) -> datetime:
|
||||
"""Convert a string from SVN's datetime format to a datetime object."""
|
||||
date, time, _timezone, _day, _n_day, _mo, _y = datetime_str.split(" ")
|
||||
@ -184,4 +188,4 @@ def svn_date_simple(datetime_str: str) -> str:
|
||||
date_str = f"{dt.year}-{month_name}-{dt.day}"
|
||||
time_str = f"{str(dt.hour).zfill(2)}:{str(dt.minute).zfill(2)}"
|
||||
|
||||
return date_str + " " + time_str
|
||||
return date_str + " " + time_str
|
||||
|
@ -1,25 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# (c) 2022, Blender Foundation - Demeter Dzadik
|
||||
|
||||
from ..svn_info import get_svn_info
|
||||
from ..util import get_addon_prefs
|
||||
from .. import constants
|
||||
from .execute_subprocess import execute_svn_command
|
||||
from .background_process import BackgroundProcess, Processes
|
||||
from bpy.types import Operator
|
||||
from bpy.props import StringProperty
|
||||
import bpy
|
||||
import xmltodict
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Union, Any, Set, Optional, Tuple
|
||||
from .. import wheels
|
||||
# This will load the xmltodict wheel file.
|
||||
wheels.preload_dependencies()
|
||||
|
||||
from typing import List, Dict, Union, Any, Set, Optional, Tuple
|
||||
from pathlib import Path
|
||||
import time
|
||||
import xmltodict
|
||||
|
||||
import bpy
|
||||
from bpy.props import StringProperty
|
||||
from bpy.types import Operator
|
||||
|
||||
from .background_process import BackgroundProcess, Processes
|
||||
from .execute_subprocess import execute_svn_command
|
||||
from .. import constants
|
||||
from ..util import get_addon_prefs
|
||||
from ..svn_info import get_svn_info
|
||||
|
||||
|
||||
class SVN_OT_explain_status(Operator):
|
||||
bl_idname = "svn.explain_status"
|
||||
@ -72,7 +69,7 @@ def init_svn_of_current_file(_scene=None):
|
||||
prefs.sync_repo_info_file()
|
||||
|
||||
for repo in prefs.repositories:
|
||||
# This would ideally only run when opening Blender for the first
|
||||
# This would ideally only run when opening Blender for the first
|
||||
# time, but there is no app handler for that, sadly.
|
||||
repo.authenticated = False
|
||||
repo.auth_failed = False
|
||||
@ -89,7 +86,7 @@ def init_svn_of_current_file(_scene=None):
|
||||
repo = scene_svn.get_scene_repo(context)
|
||||
if not repo:
|
||||
repo = prefs.init_repo(context, scene_svn.svn_directory)
|
||||
|
||||
|
||||
for i, other_repo in enumerate(prefs.repositories):
|
||||
if other_repo == repo:
|
||||
prefs.active_repo_idx = i
|
||||
@ -129,6 +126,7 @@ def set_scene_svn_info(context) -> bool:
|
||||
############## AUTOMATICALLY KEEPING FILE STATUSES UP TO DATE ##################
|
||||
################################################################################
|
||||
|
||||
|
||||
class BGP_SVN_Status(BackgroundProcess):
|
||||
name = "Status"
|
||||
needs_authentication = True
|
||||
@ -142,7 +140,7 @@ class BGP_SVN_Status(BackgroundProcess):
|
||||
|
||||
def acquire_output(self, context, prefs):
|
||||
self.output = execute_svn_command(
|
||||
context,
|
||||
context,
|
||||
["svn", "status", "--show-updates", "--verbose", "--xml"],
|
||||
use_cred=True
|
||||
)
|
||||
@ -228,7 +226,8 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
|
||||
entry_existed = False
|
||||
file_entry = repo.external_files.add()
|
||||
file_entry.svn_path = svn_path_str
|
||||
file_entry.absolute_path = str(repo.svn_to_absolute_path(svn_path).as_posix())
|
||||
file_entry.absolute_path = str(
|
||||
repo.svn_to_absolute_path(svn_path).as_posix())
|
||||
|
||||
file_entry['name'] = svn_path.name
|
||||
if not file_entry.exists:
|
||||
@ -261,7 +260,8 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
|
||||
# File entry status has changed between local and repo.
|
||||
file_strings = []
|
||||
for svn_path, repos_status in new_files_on_repo:
|
||||
status_char = constants.SVN_STATUS_NAME_TO_CHAR.get(repos_status, " ")
|
||||
status_char = constants.SVN_STATUS_NAME_TO_CHAR.get(
|
||||
repos_status, " ")
|
||||
file_strings.append(f"{status_char} {svn_path}")
|
||||
print(
|
||||
"SVN: Detected file changes on remote:\n",
|
||||
|
@ -25,7 +25,7 @@ class BGP_SVN_Update(BackgroundProcess):
|
||||
if self.revision > 0:
|
||||
command.insert(2, f"-r{self.revision}")
|
||||
self.output = execute_svn_command(
|
||||
context,
|
||||
context,
|
||||
command,
|
||||
use_cred=True
|
||||
)
|
||||
@ -44,7 +44,6 @@ class BGP_SVN_Update(BackgroundProcess):
|
||||
Processes.start('Log')
|
||||
Processes.start('Status')
|
||||
|
||||
|
||||
def get_ui_message(self, context) -> str:
|
||||
"""Return a string that should be drawn in the UI for user feedback,
|
||||
depending on the state of the process."""
|
||||
|
@ -16,4 +16,4 @@ modules = [
|
||||
ui_prefs,
|
||||
ui_outdated_warning,
|
||||
ui_context_menus
|
||||
]
|
||||
]
|
||||
|
@ -6,6 +6,7 @@ from bpy.types import Context, UIList, Operator
|
||||
from bpy.props import StringProperty
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class SVN_OT_open_blend_file(Operator):
|
||||
# This is needed because drawing a button for wm.open_mainfile in the UI
|
||||
# directly simply does not work; Blender just opens a full-screen filebrowser,
|
||||
@ -18,7 +19,7 @@ class SVN_OT_open_blend_file(Operator):
|
||||
filepath: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.open_mainfile(filepath=self.filepath, load_ui = False)
|
||||
bpy.ops.wm.open_mainfile(filepath=self.filepath, load_ui=False)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@ -39,7 +40,7 @@ def svn_file_list_context_menu(self: UIList, context: Context) -> None:
|
||||
active_file = context.scene.svn.get_repo(context).active_file
|
||||
if active_file.name.endswith("blend"):
|
||||
layout.operator("svn.open_blend_file",
|
||||
text=f"Open {active_file.name}").filepath = active_file.absolute_path
|
||||
text=f"Open {active_file.name}").filepath = active_file.absolute_path
|
||||
else:
|
||||
layout.operator("wm.path_open",
|
||||
text=f"Open {active_file.name}").filepath = str(Path(active_file.absolute_path))
|
||||
@ -67,8 +68,10 @@ def register():
|
||||
bpy.types.UI_MT_list_item_context_menu.append(svn_file_list_context_menu)
|
||||
bpy.types.UI_MT_list_item_context_menu.append(svn_log_list_context_menu)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.UI_MT_list_item_context_menu.remove(svn_file_list_context_menu)
|
||||
bpy.types.UI_MT_list_item_context_menu.remove(svn_log_list_context_menu)
|
||||
|
||||
registry = [SVN_OT_open_blend_file]
|
||||
|
||||
registry = [SVN_OT_open_blend_file]
|
||||
|
@ -122,7 +122,8 @@ class SVN_UL_file_list(UIList):
|
||||
element becomes hidden."""
|
||||
flt_neworder = []
|
||||
list_items = getattr(data, propname)
|
||||
flt_flags = [file.show_in_filelist * cls.UILST_FLT_ITEM for file in list_items]
|
||||
flt_flags = [file.show_in_filelist *
|
||||
cls.UILST_FLT_ITEM for file in list_items]
|
||||
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
|
||||
@ -147,7 +148,8 @@ class SVN_UL_file_list(UIList):
|
||||
|
||||
row.prop(self, 'show_file_paths', text="",
|
||||
toggle=True, icon="FILE_FOLDER")
|
||||
row.prop(context.scene.svn.get_repo(context), 'file_search_filter', text="")
|
||||
row.prop(context.scene.svn.get_repo(context),
|
||||
'file_search_filter', text="")
|
||||
|
||||
|
||||
def draw_process_info(context, layout):
|
||||
@ -167,7 +169,7 @@ def draw_process_info(context, layout):
|
||||
warning.process_id = process.name
|
||||
any_error = True
|
||||
break
|
||||
|
||||
|
||||
if process.is_running:
|
||||
message = process.get_ui_message(context)
|
||||
if message:
|
||||
@ -177,7 +179,8 @@ def draw_process_info(context, layout):
|
||||
if not any_error and process_message:
|
||||
col.label(text=process_message)
|
||||
if prefs.debug_mode:
|
||||
col.label(text="Processes: " + ", ".join([p.name for p in Processes.running_processes]))
|
||||
col.label(text="Processes: " +
|
||||
", ".join([p.name for p in Processes.running_processes]))
|
||||
|
||||
|
||||
def draw_repo_file_list(context, layout, repo):
|
||||
@ -223,7 +226,7 @@ def draw_repo_file_list(context, layout, repo):
|
||||
|
||||
col.separator()
|
||||
col.operator("svn.commit", icon='EXPORT', text="")
|
||||
col.operator("svn.update_all", icon='IMPORT', text="").revision=0
|
||||
col.operator("svn.update_all", icon='IMPORT', text="").revision = 0
|
||||
|
||||
col.separator()
|
||||
col.operator("svn.cleanup", icon='BRUSH_DATA', text="")
|
||||
|
@ -67,4 +67,3 @@ registry = [
|
||||
FILEBROWSER_PT_SVN_files,
|
||||
FILEBROWSER_PT_SVN_log
|
||||
]
|
||||
|
||||
|
@ -62,14 +62,15 @@ class SVN_UL_log(UIList):
|
||||
|
||||
if not self.show_all_logs:
|
||||
flt_flags = [
|
||||
log_entry.affects_active_file * self.bitflag_filter_item
|
||||
log_entry.affects_active_file * self.bitflag_filter_item
|
||||
for log_entry in log_entries
|
||||
]
|
||||
|
||||
if self.filter_name:
|
||||
# Filtering: Allow comma-separated keywords.
|
||||
# ALL keywords must be found somewhere in the log entry for it to show up.
|
||||
filter_words = [word.strip().lower() for word in self.filter_name.split(",")]
|
||||
filter_words = [word.strip().lower()
|
||||
for word in self.filter_name.split(",")]
|
||||
for idx, log_entry in enumerate(log_entries):
|
||||
for filter_word in filter_words:
|
||||
if filter_word not in log_entry.text_to_search:
|
||||
@ -269,4 +270,4 @@ registry = [
|
||||
SVN_UL_log,
|
||||
SVN_OT_log_tooltip,
|
||||
SVN_OT_log_show_commit_msg
|
||||
]
|
||||
]
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def draw_outdated_file_warning(self, context):
|
||||
repo = context.scene.svn.get_repo(context)
|
||||
if not repo:
|
||||
@ -31,6 +32,7 @@ def draw_outdated_file_warning(self, context):
|
||||
'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"
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.VIEW3D_HT_header.prepend(draw_outdated_file_warning)
|
||||
|
||||
|
@ -12,6 +12,7 @@ from .ui_file_list import draw_repo_file_list, draw_process_info
|
||||
from ..threaded.background_process import Processes
|
||||
import platform
|
||||
|
||||
|
||||
class SVN_UL_repositories(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
repo = item
|
||||
@ -26,6 +27,7 @@ class SVN_UL_repositories(UIList):
|
||||
row.alert = True
|
||||
row.prop(repo, 'directory', text="")
|
||||
|
||||
|
||||
class SVN_OT_repo_add(Operator, ImportHelper):
|
||||
"""Add a repository to the list"""
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
@ -45,7 +47,8 @@ class SVN_OT_repo_add(Operator, ImportHelper):
|
||||
try:
|
||||
repo = prefs.init_repo(context, path)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, "Failed to initialize repository. Ensure you have SVN installed, and that the selected directory is the root of a repository.")
|
||||
self.report(
|
||||
{'ERROR'}, "Failed to initialize repository. Ensure you have SVN installed, and that the selected directory is the root of a repository.")
|
||||
print(e)
|
||||
return {'CANCELLED'}
|
||||
if not repo:
|
||||
@ -59,6 +62,7 @@ class SVN_OT_repo_add(Operator, ImportHelper):
|
||||
prefs.save_repo_info_to_file()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SVN_OT_repo_remove(Operator):
|
||||
"""Remove a repository from the list"""
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
@ -81,14 +85,17 @@ class SVN_OT_repo_remove(Operator):
|
||||
prefs.save_repo_info_to_file()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SVN_MT_add_repo(Menu):
|
||||
bl_idname = "SVN_MT_add_repo"
|
||||
bl_label = "Add Repo"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("svn.repo_add", text="Browse Existing Checkout", icon='FILE_FOLDER')
|
||||
layout.operator("svn.checkout_initiate", text="Create New Checkout", icon='URL').create=True
|
||||
layout.operator(
|
||||
"svn.repo_add", text="Browse Existing Checkout", icon='FILE_FOLDER')
|
||||
layout.operator("svn.checkout_initiate",
|
||||
text="Create New Checkout", icon='URL').create = True
|
||||
|
||||
|
||||
def draw_prefs(self, context):
|
||||
@ -103,7 +110,7 @@ def draw_prefs_checkout(self, context):
|
||||
msg_windows = "If you don't, cancel this operation and toggle it using Window->Toggle System Console."
|
||||
msg_linux = "If you don't, quit Blender and re-launch it from a terminal."
|
||||
msg_mac = msg_linux
|
||||
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
return msg_windows
|
||||
@ -120,13 +127,17 @@ def draw_prefs_checkout(self, context):
|
||||
col.label(text="Make sure you have Blender's terminal open!")
|
||||
col.label(text=get_terminal_howto())
|
||||
col.separator()
|
||||
col.label(text="Downloading a repository can take a long time, and the UI will be locked.")
|
||||
col.label(text="Without a terminal, you won't be able to track the progress of the checkout.")
|
||||
col.label(
|
||||
text="Downloading a repository can take a long time, and the UI will be locked.")
|
||||
col.label(
|
||||
text="Without a terminal, you won't be able to track the progress of the checkout.")
|
||||
col.separator()
|
||||
|
||||
col = layout.column()
|
||||
col.label(text="To interrupt the checkout, you can press Ctrl+C in the terminal.", icon='INFO')
|
||||
col.label(text="You can resume it by re-running this operation, or with the SVN Update button.", icon='INFO')
|
||||
col.label(
|
||||
text="To interrupt the checkout, you can press Ctrl+C in the terminal.", icon='INFO')
|
||||
col.label(
|
||||
text="You can resume it by re-running this operation, or with the SVN Update button.", icon='INFO')
|
||||
col.separator()
|
||||
|
||||
prefs = get_addon_prefs(context)
|
||||
@ -137,8 +148,9 @@ def draw_prefs_checkout(self, context):
|
||||
continue
|
||||
if other_repo.directory == repo.directory:
|
||||
row = col.row()
|
||||
row.alert=True
|
||||
row.label(text="A repository at this filepath is already specified.", icon='ERROR')
|
||||
row.alert = True
|
||||
row.label(
|
||||
text="A repository at this filepath is already specified.", icon='ERROR')
|
||||
break
|
||||
|
||||
col.prop(repo, 'display_name', text="Folder Name", icon='NEWFOLDER')
|
||||
@ -148,9 +160,10 @@ def draw_prefs_checkout(self, context):
|
||||
continue
|
||||
if other_repo.url == repo.url:
|
||||
sub = col.column()
|
||||
sub.alert=True
|
||||
sub.alert = True
|
||||
sub.label(text="A repository with this URL is already specified.")
|
||||
sub.label(text="If you're sure you want to checkout another copy of the repo, feel free to proceed.")
|
||||
sub.label(
|
||||
text="If you're sure you want to checkout another copy of the repo, feel free to proceed.")
|
||||
break
|
||||
col.prop(repo, 'username', icon='USER')
|
||||
col.prop(repo, 'password', icon='LOCKED')
|
||||
@ -159,6 +172,7 @@ def draw_prefs_checkout(self, context):
|
||||
op_row.operator('svn.checkout_finalize', text="Checkout", icon='CHECKMARK')
|
||||
op_row.operator('svn.checkout_cancel', text="Cancel", icon="X")
|
||||
|
||||
|
||||
def draw_prefs_repos(self, context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
@ -224,10 +238,12 @@ def draw_prefs_repos(self, context) -> None:
|
||||
draw_repo_error(layout, "Directory is not an SVN repository.")
|
||||
split = layout.split(factor=0.24)
|
||||
split.row()
|
||||
split.row().operator("svn.checkout_initiate", text="Create New Checkout", icon='URL').create=False
|
||||
split.row().operator("svn.checkout_initiate",
|
||||
text="Create New Checkout", icon='URL').create = False
|
||||
return
|
||||
if not self.active_repo.authenticated and not auth_in_progress and not auth_error:
|
||||
draw_repo_error(layout, "Repository not authenticated. Enter your credentials.")
|
||||
draw_repo_error(
|
||||
layout, "Repository not authenticated. Enter your credentials.")
|
||||
return
|
||||
|
||||
if len(self.repositories) > 0 and self.active_repo.authenticated:
|
||||
@ -240,16 +256,18 @@ def draw_prefs_repos(self, context) -> None:
|
||||
layout.label(text="Log: ")
|
||||
draw_svn_log(context, layout, file_browser=False)
|
||||
|
||||
|
||||
def draw_repo_error(layout, message):
|
||||
split = layout.split(factor=0.24)
|
||||
split.row()
|
||||
col = split.column()
|
||||
col.alert=True
|
||||
col.alert = True
|
||||
col.label(text=message, icon='ERROR')
|
||||
|
||||
|
||||
registry = [
|
||||
SVN_UL_repositories,
|
||||
SVN_OT_repo_add,
|
||||
SVN_OT_repo_remove,
|
||||
SVN_MT_add_repo
|
||||
]
|
||||
]
|
||||
|
@ -67,4 +67,3 @@ registry = [
|
||||
VIEW3D_PT_svn_credentials,
|
||||
VIEW3D_PT_svn_files,
|
||||
]
|
||||
|
||||
|
@ -8,12 +8,15 @@ import bpy
|
||||
|
||||
package_name = __package__
|
||||
|
||||
|
||||
def get_addon_prefs(context):
|
||||
return context.preferences.addons[__package__].preferences
|
||||
|
||||
|
||||
def dots():
|
||||
return "." * int((time() % 10) + 3)
|
||||
|
||||
|
||||
def redraw_viewport(context=None) -> None:
|
||||
"""This causes the sidebar UI to refresh without having to mouse-hover it."""
|
||||
context = bpy.context
|
||||
|
Loading…
Reference in New Issue
Block a user