diff --git a/src/blenderbim/blenderbim/bim/module/ifcgit/__init__.py b/src/blenderbim/blenderbim/bim/module/ifcgit/__init__.py index ad0337832a..eba6e3af33 100644 --- a/src/blenderbim/blenderbim/bim/module/ifcgit/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/ifcgit/__init__.py @@ -22,6 +22,7 @@ from . import ui, prop, operator classes = ( operator.AddFileToRepo, operator.AddTag, + operator.CloneRepo, operator.CommitChanges, operator.CreateRepo, operator.DiscardUncommitted, diff --git a/src/blenderbim/blenderbim/bim/module/ifcgit/operator.py b/src/blenderbim/blenderbim/bim/module/ifcgit/operator.py index 9bf88c58d4..1df3063755 100644 --- a/src/blenderbim/blenderbim/bim/module/ifcgit/operator.py +++ b/src/blenderbim/blenderbim/bim/module/ifcgit/operator.py @@ -57,6 +57,33 @@ class AddFileToRepo(bpy.types.Operator): 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): """Discard saved changes and update to HEAD""" diff --git a/src/blenderbim/blenderbim/bim/module/ifcgit/prop.py b/src/blenderbim/blenderbim/bim/module/ifcgit/prop.py index 0780e6df3e..88fcd68aaa 100644 --- a/src/blenderbim/blenderbim/bim/module/ifcgit/prop.py +++ b/src/blenderbim/blenderbim/bim/module/ifcgit/prop.py @@ -97,6 +97,17 @@ class IfcGitProperties(PropertyGroup): description="An optional human readable description of this tag", 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) ifcgit_filter: EnumProperty( items=[ diff --git a/src/blenderbim/blenderbim/bim/module/ifcgit/ui.py b/src/blenderbim/blenderbim/bim/module/ifcgit/ui.py index ddae10e108..f9b67cfc7c 100644 --- a/src/blenderbim/blenderbim/bim/module/ifcgit/ui.py +++ b/src/blenderbim/blenderbim/bim/module/ifcgit/ui.py @@ -54,6 +54,16 @@ class IFCGIT_PT_panel(bpy.types.Panel): else: row.label(text="No Git repository found", icon="SYSTEM") 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 is_dirty = IfcGitData.data["is_dirty"] diff --git a/src/blenderbim/blenderbim/core/ifcgit.py b/src/blenderbim/blenderbim/core/ifcgit.py index deb0358c91..955ba064e5 100644 --- a/src/blenderbim/blenderbim/core/ifcgit.py +++ b/src/blenderbim/blenderbim/core/ifcgit.py @@ -10,6 +10,15 @@ def add_file(ifcgit, 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): path_ifc = ifc.get_path() # 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): - ifcgit.clear_commits_list() - path_ifc = ifc.get_path() - lookup = ifcgit.tags_by_hexsha(repo) - ifcgit.get_commits_list(path_ifc, lookup) + ifcgit.refresh_revision_list(ifc.get_path()) def colourise_revision(ifcgit, context): @@ -58,6 +64,7 @@ def switch_revision(ifcgit, ifc): path_ifc = ifc.get_path() ifcgit.switch_to_revision_item() ifcgit.load_project(path_ifc) + ifcgit.refresh_revision_list(path_ifc) def merge_branch(ifcgit, ifc, operator): diff --git a/src/blenderbim/blenderbim/tool/ifcgit.py b/src/blenderbim/blenderbim/tool/ifcgit.py index 7826ab5c75..f37d9289ba 100644 --- a/src/blenderbim/blenderbim/tool/ifcgit.py +++ b/src/blenderbim/blenderbim/tool/ifcgit.py @@ -14,6 +14,21 @@ class IfcGit: def init_repo(cls, 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 def get_path_dir(cls, path_ifc): return os.path.abspath(os.path.dirname(path_ifc)) @@ -39,7 +54,7 @@ class IfcGit: if parentdir_path == path_dir: # root folder return None - return IfcGit.repo_from_path(parentdir_path) + return cls.repo_from_path(parentdir_path) if repo: IfcGitRepo.repo = repo return repo @@ -94,15 +109,16 @@ class IfcGit: def get_commits_list(cls, path_ifc, lookup): props = bpy.context.scene.IfcGitProperties + repo = cls.repo_from_path(path_ifc) commits = list( git.objects.commit.Commit.iter_items( - repo=IfcGitRepo.repo, + repo=repo, rev=[props.display_branch], ) ) commits_relevant = list( git.objects.commit.Commit.iter_items( - repo=IfcGitRepo.repo, + repo=repo, rev=[props.display_branch], paths=[path_ifc], ) @@ -131,6 +147,13 @@ class IfcGit: # a lightweight tag has no 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 def is_valid_ref_format(cls, string): """Check a bare branch or tag name is valid""" @@ -148,7 +171,7 @@ class IfcGit: # delete any IfcProject/* collections for collection in bpy.data.collections: if re.match("^IfcProject/", collection.name): - IfcGit.delete_collection(collection) + cls.delete_collection(collection) # delete any Ifc* objects not in IfcProject/ heirarchy for obj in bpy.data.objects: if re.match("^Ifc", obj.name): @@ -157,7 +180,6 @@ class IfcGit: bpy.data.orphans_purge(do_recursive=True) bpy.ops.bim.load_project(filepath=path_ifc) - bpy.ops.ifcgit.refresh() @classmethod def branches_by_hexsha(cls, repo): @@ -230,14 +252,14 @@ class IfcGit: return if current_revision.committed_date > selected_revision.committed_date: - step_ids = IfcGit.ifc_diff_ids( + step_ids = cls.ifc_diff_ids( repo, selected_revision.hexsha, current_revision.hexsha, path_ifc, ) else: - step_ids = IfcGit.ifc_diff_ids( + step_ids = cls.ifc_diff_ids( repo, current_revision.hexsha, selected_revision.hexsha, @@ -290,7 +312,7 @@ class IfcGit: repo = IfcGitRepo.repo item = props.ifcgit_commits[props.commit_index] - lookup = IfcGit.branches_by_hexsha(repo) + lookup = cls.branches_by_hexsha(repo) if item.hexsha in lookup: for branch in lookup[item.hexsha]: if branch.name == props.display_branch: @@ -308,7 +330,7 @@ class IfcGit: @classmethod def is_valid_branch_name(cls, new_branch_name): """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 if new_branch_name in [branch.name for branch in IfcGitRepo.repo.branches]: return False @@ -328,7 +350,7 @@ class IfcGit: props = bpy.context.scene.IfcGitProperties repo = IfcGitRepo.repo item = props.ifcgit_commits[props.commit_index] - lookup = IfcGit.branches_by_hexsha(repo) + lookup = cls.branches_by_hexsha(repo) if item.hexsha in lookup: for branch in lookup[item.hexsha]: if branch.name == props.display_branch: @@ -356,7 +378,8 @@ class IfcGit: props.commit_message = "Merged branch: " + props.display_branch 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: