mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 09:08:40 +00:00
BlenderBIM Git clone repository operator (#3096)
This commit is contained in:
@@ -22,6 +22,7 @@ from . import ui, prop, operator
|
|||||||
classes = (
|
classes = (
|
||||||
operator.AddFileToRepo,
|
operator.AddFileToRepo,
|
||||||
operator.AddTag,
|
operator.AddTag,
|
||||||
|
operator.CloneRepo,
|
||||||
operator.CommitChanges,
|
operator.CommitChanges,
|
||||||
operator.CreateRepo,
|
operator.CreateRepo,
|
||||||
operator.DiscardUncommitted,
|
operator.DiscardUncommitted,
|
||||||
|
|||||||
@@ -57,6 +57,33 @@ class AddFileToRepo(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class CloneRepo(bpy.types.Operator):
|
||||||
|
"""Clone a remote Git repository"""
|
||||||
|
|
||||||
|
bl_label = "Clone repository"
|
||||||
|
bl_idname = "ifcgit.clone_repo"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.IfcGitProperties
|
||||||
|
if (
|
||||||
|
props.remote_url
|
||||||
|
and props.local_folder
|
||||||
|
and os.path.isdir(props.local_folder)
|
||||||
|
and not os.listdir(props.local_folder)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
|
||||||
|
props = context.scene.IfcGitProperties
|
||||||
|
core.clone_repo(tool.IfcGit, props.remote_url, props.local_folder, self)
|
||||||
|
refresh()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class DiscardUncommitted(bpy.types.Operator):
|
class DiscardUncommitted(bpy.types.Operator):
|
||||||
"""Discard saved changes and update to HEAD"""
|
"""Discard saved changes and update to HEAD"""
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ class IfcGitProperties(PropertyGroup):
|
|||||||
description="An optional human readable description of this tag",
|
description="An optional human readable description of this tag",
|
||||||
default="",
|
default="",
|
||||||
)
|
)
|
||||||
|
remote_url: StringProperty(
|
||||||
|
name="Git URL",
|
||||||
|
description="A URL pointing to a Git repository",
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
local_folder: StringProperty(
|
||||||
|
name="Local folder",
|
||||||
|
description="A local Git repository path",
|
||||||
|
default="",
|
||||||
|
subtype="DIR_PATH",
|
||||||
|
)
|
||||||
display_branch: EnumProperty(items=git_branches, update=update_revlist)
|
display_branch: EnumProperty(items=git_branches, update=update_revlist)
|
||||||
ifcgit_filter: EnumProperty(
|
ifcgit_filter: EnumProperty(
|
||||||
items=[
|
items=[
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
|||||||
else:
|
else:
|
||||||
row.label(text="No Git repository found", icon="SYSTEM")
|
row.label(text="No Git repository found", icon="SYSTEM")
|
||||||
row.label(text="No IFC project saved", icon="FILE")
|
row.label(text="No IFC project saved", icon="FILE")
|
||||||
|
|
||||||
|
box = layout.box()
|
||||||
|
row = box.row()
|
||||||
|
row.label(text="Clone a remote Git repository")
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "remote_url")
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "local_folder")
|
||||||
|
row = box.row()
|
||||||
|
row.operator("ifcgit.clone_repo", icon="IMPORT")
|
||||||
return
|
return
|
||||||
|
|
||||||
is_dirty = IfcGitData.data["is_dirty"]
|
is_dirty = IfcGitData.data["is_dirty"]
|
||||||
|
|||||||
@@ -10,6 +10,15 @@ def add_file(ifcgit, ifc):
|
|||||||
ifcgit.add_file_to_repo(repo, path_ifc)
|
ifcgit.add_file_to_repo(repo, path_ifc)
|
||||||
|
|
||||||
|
|
||||||
|
def clone_repo(ifcgit, remote_url, local_folder, operator):
|
||||||
|
repo = ifcgit.clone_repo(remote_url, local_folder)
|
||||||
|
if not repo:
|
||||||
|
operator.report({"ERROR"}, "Clone failed")
|
||||||
|
return
|
||||||
|
operator.report({"INFO"}, "Repository cloned")
|
||||||
|
ifcgit.load_anyifc(repo)
|
||||||
|
|
||||||
|
|
||||||
def discard_uncomitted(ifcgit, ifc):
|
def discard_uncomitted(ifcgit, ifc):
|
||||||
path_ifc = ifc.get_path()
|
path_ifc = ifc.get_path()
|
||||||
# NOTE this is calling the git binary in a subprocess
|
# NOTE this is calling the git binary in a subprocess
|
||||||
@@ -30,10 +39,7 @@ def add_tag(ifcgit, repo):
|
|||||||
|
|
||||||
|
|
||||||
def refresh_revision_list(ifcgit, repo, ifc):
|
def refresh_revision_list(ifcgit, repo, ifc):
|
||||||
ifcgit.clear_commits_list()
|
ifcgit.refresh_revision_list(ifc.get_path())
|
||||||
path_ifc = ifc.get_path()
|
|
||||||
lookup = ifcgit.tags_by_hexsha(repo)
|
|
||||||
ifcgit.get_commits_list(path_ifc, lookup)
|
|
||||||
|
|
||||||
|
|
||||||
def colourise_revision(ifcgit, context):
|
def colourise_revision(ifcgit, context):
|
||||||
@@ -58,6 +64,7 @@ def switch_revision(ifcgit, ifc):
|
|||||||
path_ifc = ifc.get_path()
|
path_ifc = ifc.get_path()
|
||||||
ifcgit.switch_to_revision_item()
|
ifcgit.switch_to_revision_item()
|
||||||
ifcgit.load_project(path_ifc)
|
ifcgit.load_project(path_ifc)
|
||||||
|
ifcgit.refresh_revision_list(path_ifc)
|
||||||
|
|
||||||
|
|
||||||
def merge_branch(ifcgit, ifc, operator):
|
def merge_branch(ifcgit, ifc, operator):
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ class IfcGit:
|
|||||||
def init_repo(cls, path_dir):
|
def init_repo(cls, path_dir):
|
||||||
IfcGitRepo.repo = git.Repo.init(path_dir)
|
IfcGitRepo.repo = git.Repo.init(path_dir)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clone_repo(cls, remote_url, local_folder):
|
||||||
|
IfcGitRepo.repo = git.Repo.clone_from(remote_url, local_folder)
|
||||||
|
return IfcGitRepo.repo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_anyifc(cls, repo):
|
||||||
|
working_dir = repo.working_dir
|
||||||
|
for item in os.listdir(working_dir):
|
||||||
|
path = os.path.join(working_dir, item)
|
||||||
|
if os.path.isfile(path) and re.match(".*\.ifc$", path, re.IGNORECASE):
|
||||||
|
cls.load_project(path)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_path_dir(cls, path_ifc):
|
def get_path_dir(cls, path_ifc):
|
||||||
return os.path.abspath(os.path.dirname(path_ifc))
|
return os.path.abspath(os.path.dirname(path_ifc))
|
||||||
@@ -39,7 +54,7 @@ class IfcGit:
|
|||||||
if parentdir_path == path_dir:
|
if parentdir_path == path_dir:
|
||||||
# root folder
|
# root folder
|
||||||
return None
|
return None
|
||||||
return IfcGit.repo_from_path(parentdir_path)
|
return cls.repo_from_path(parentdir_path)
|
||||||
if repo:
|
if repo:
|
||||||
IfcGitRepo.repo = repo
|
IfcGitRepo.repo = repo
|
||||||
return repo
|
return repo
|
||||||
@@ -94,15 +109,16 @@ class IfcGit:
|
|||||||
def get_commits_list(cls, path_ifc, lookup):
|
def get_commits_list(cls, path_ifc, lookup):
|
||||||
|
|
||||||
props = bpy.context.scene.IfcGitProperties
|
props = bpy.context.scene.IfcGitProperties
|
||||||
|
repo = cls.repo_from_path(path_ifc)
|
||||||
commits = list(
|
commits = list(
|
||||||
git.objects.commit.Commit.iter_items(
|
git.objects.commit.Commit.iter_items(
|
||||||
repo=IfcGitRepo.repo,
|
repo=repo,
|
||||||
rev=[props.display_branch],
|
rev=[props.display_branch],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
commits_relevant = list(
|
commits_relevant = list(
|
||||||
git.objects.commit.Commit.iter_items(
|
git.objects.commit.Commit.iter_items(
|
||||||
repo=IfcGitRepo.repo,
|
repo=repo,
|
||||||
rev=[props.display_branch],
|
rev=[props.display_branch],
|
||||||
paths=[path_ifc],
|
paths=[path_ifc],
|
||||||
)
|
)
|
||||||
@@ -131,6 +147,13 @@ class IfcGit:
|
|||||||
# a lightweight tag has no message
|
# a lightweight tag has no message
|
||||||
list_item.tags[-1].message = tag.tag.message
|
list_item.tags[-1].message = tag.tag.message
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def refresh_revision_list(cls, path_ifc):
|
||||||
|
repo = cls.repo_from_path(path_ifc)
|
||||||
|
cls.clear_commits_list()
|
||||||
|
lookup = cls.tags_by_hexsha(repo)
|
||||||
|
cls.get_commits_list(path_ifc, lookup)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_valid_ref_format(cls, string):
|
def is_valid_ref_format(cls, string):
|
||||||
"""Check a bare branch or tag name is valid"""
|
"""Check a bare branch or tag name is valid"""
|
||||||
@@ -148,7 +171,7 @@ class IfcGit:
|
|||||||
# delete any IfcProject/* collections
|
# delete any IfcProject/* collections
|
||||||
for collection in bpy.data.collections:
|
for collection in bpy.data.collections:
|
||||||
if re.match("^IfcProject/", collection.name):
|
if re.match("^IfcProject/", collection.name):
|
||||||
IfcGit.delete_collection(collection)
|
cls.delete_collection(collection)
|
||||||
# delete any Ifc* objects not in IfcProject/ heirarchy
|
# delete any Ifc* objects not in IfcProject/ heirarchy
|
||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
if re.match("^Ifc", obj.name):
|
if re.match("^Ifc", obj.name):
|
||||||
@@ -157,7 +180,6 @@ class IfcGit:
|
|||||||
bpy.data.orphans_purge(do_recursive=True)
|
bpy.data.orphans_purge(do_recursive=True)
|
||||||
|
|
||||||
bpy.ops.bim.load_project(filepath=path_ifc)
|
bpy.ops.bim.load_project(filepath=path_ifc)
|
||||||
bpy.ops.ifcgit.refresh()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def branches_by_hexsha(cls, repo):
|
def branches_by_hexsha(cls, repo):
|
||||||
@@ -230,14 +252,14 @@ class IfcGit:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if current_revision.committed_date > selected_revision.committed_date:
|
if current_revision.committed_date > selected_revision.committed_date:
|
||||||
step_ids = IfcGit.ifc_diff_ids(
|
step_ids = cls.ifc_diff_ids(
|
||||||
repo,
|
repo,
|
||||||
selected_revision.hexsha,
|
selected_revision.hexsha,
|
||||||
current_revision.hexsha,
|
current_revision.hexsha,
|
||||||
path_ifc,
|
path_ifc,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
step_ids = IfcGit.ifc_diff_ids(
|
step_ids = cls.ifc_diff_ids(
|
||||||
repo,
|
repo,
|
||||||
current_revision.hexsha,
|
current_revision.hexsha,
|
||||||
selected_revision.hexsha,
|
selected_revision.hexsha,
|
||||||
@@ -290,7 +312,7 @@ class IfcGit:
|
|||||||
repo = IfcGitRepo.repo
|
repo = IfcGitRepo.repo
|
||||||
item = props.ifcgit_commits[props.commit_index]
|
item = props.ifcgit_commits[props.commit_index]
|
||||||
|
|
||||||
lookup = IfcGit.branches_by_hexsha(repo)
|
lookup = cls.branches_by_hexsha(repo)
|
||||||
if item.hexsha in lookup:
|
if item.hexsha in lookup:
|
||||||
for branch in lookup[item.hexsha]:
|
for branch in lookup[item.hexsha]:
|
||||||
if branch.name == props.display_branch:
|
if branch.name == props.display_branch:
|
||||||
@@ -308,7 +330,7 @@ class IfcGit:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def is_valid_branch_name(cls, new_branch_name):
|
def is_valid_branch_name(cls, new_branch_name):
|
||||||
"""Check if a branch name is valid and doesn't conflict with existing branches"""
|
"""Check if a branch name is valid and doesn't conflict with existing branches"""
|
||||||
if not IfcGit.is_valid_ref_format(new_branch_name):
|
if not cls.is_valid_ref_format(new_branch_name):
|
||||||
return False
|
return False
|
||||||
if new_branch_name in [branch.name for branch in IfcGitRepo.repo.branches]:
|
if new_branch_name in [branch.name for branch in IfcGitRepo.repo.branches]:
|
||||||
return False
|
return False
|
||||||
@@ -328,7 +350,7 @@ class IfcGit:
|
|||||||
props = bpy.context.scene.IfcGitProperties
|
props = bpy.context.scene.IfcGitProperties
|
||||||
repo = IfcGitRepo.repo
|
repo = IfcGitRepo.repo
|
||||||
item = props.ifcgit_commits[props.commit_index]
|
item = props.ifcgit_commits[props.commit_index]
|
||||||
lookup = IfcGit.branches_by_hexsha(repo)
|
lookup = cls.branches_by_hexsha(repo)
|
||||||
if item.hexsha in lookup:
|
if item.hexsha in lookup:
|
||||||
for branch in lookup[item.hexsha]:
|
for branch in lookup[item.hexsha]:
|
||||||
if branch.name == props.display_branch:
|
if branch.name == props.display_branch:
|
||||||
@@ -356,7 +378,8 @@ class IfcGit:
|
|||||||
props.commit_message = "Merged branch: " + props.display_branch
|
props.commit_message = "Merged branch: " + props.display_branch
|
||||||
props.display_branch = repo.active_branch.name
|
props.display_branch = repo.active_branch.name
|
||||||
|
|
||||||
IfcGit.load_project(path_ifc)
|
cls.load_project(path_ifc)
|
||||||
|
cls.refresh_revision_list(path_ifc)
|
||||||
|
|
||||||
|
|
||||||
class IfcGitRepo:
|
class IfcGitRepo:
|
||||||
|
|||||||
Reference in New Issue
Block a user