最佳实践
何时恢复 Git 项目 CODESYS 编辑项目时崩溃了
取决于哪种操作 CODESYS 以前崩溃过,你可以选择以下恢复:
通常,你可以在崩溃后重新打开项目。 CODESYS Git 恢复最近一次内部保存的项目状态,并将项目内容重写到 Git 项目存储中。这种 “故障保存” 机制将项目视为 “主” 数据源。如果项目在合并操作期间崩溃,则不应在重新打开后继续工作之前立即保存,而应先丢弃所有更改(git reset--hard)。为此,你可以使用 丢弃所有更改 命令在 状态和阶段 观点。
如果崩溃后无法再成功打开项目(例如,因为完全不兼容的文件最终出现在项目存储中),您可以选择以下恢复选项:从存储库完全重新加载项目。有关更多信息,请参阅: 从存储库重建项目
将项目转移自 CODESYS SVN 到 CODESYS Git
我们强烈建议您在成功将项目转移到 GIT 后不要删除 SVN 存储库,这样在必要时您仍然可以访问它们。
提示
在 CODESYS Git 版本 1.4.0.0 及更高版本,脚本接口可用于从中传输项目 CODESYS SVN 到 CODESYS Git。
重要
Git 命令 git-svn
不能用来转移 CODESYS项目来自 CODESYS SVN 到 CODESYS Git 因为 CODESYS Git 使用 .json
文件格式,与二进制文件格式的对比 CODESYS SVN。
在 CODESYS Git,一个 Git 存储库只能管理一个 CODESYS 项目。因为一个 SVN 存储库可以包含 CODESYS 项目,传输到时,必须为 SVN 存储库中的每个项目创建单独的 Git 存储库 CODESYS Git。
建议将 SVN 存储库的各个项目转移到的存储库中 CODESYS Git 在逐个项目的基础上。
不建议将 SVN 存储库拆分成多个 SVN 存储库,因为这可能会导致以后出现问题。
分支和标签存在于 SVN 和 GIT 中,但它们在这两个系统中的设计不同。向... 转账
git-svn
命令不可用,因为此命令不能用于 CODESYS Git (见上文)。因此,我们只能将 SVN 主干转移到 Git 分支(默认为 “主分支” 或 “主分支”)。如有必要,可以手动将 SVN 标签添加为 Git 标签
在此过程中,最新版本的 CODESYS 项目首次签出 CODESYS SVN。然后断开与 SVN 的连接,并使用 Git 存储库 CODESYS Git 已为项目初始化。此过程的缺点是项目的历史记录会丢失,因为只有最新的 SVN 版本被传输到 Git
要求: CODESYS SVN 和 CODESYS Git 安装在 CODESYS。借助 工具 → 自定义 命令, 提交完成 命令已添加到 Git 集成 中的命令类别 Git 菜单。
打开 CODESYS 你保存在SVN存储库中的项目。
点击 项目 → SVN → 结账。
该项目的最新版本已签出。
点击 项目 → SVN → 断开项目与 SVN 的连接。
在计算机的本地文件目录中创建一个新的空文件夹。
在 CODESYS,点击 Git → 初始化 Git 存储库 然后在对话框中选择上一步中创建的空文件夹。
点击 Git → 提交完成。
该项目现在存储在本地 Git 存储库中,必要时可以推送到远程存储库。
在此过程中,每个 SVN 修订都是在一个 Git 提交中手动传输的。
要求: CODESYS SVN 和 CODESYS Git 安装在 CODESYS。借助 工具 → 自定义 命令, 提交完成 命令已添加到 Git 集成 中的命令类别 Git 菜单。
在中创建一个新的空项目 CODESYS 然后给它起个名字,例如,
Main Project
。点击 Git → 初始化 Git 存储库。
点击 Git → 提交完成。
点击 Git → 提交 执行空提交。 提交分阶段更改 对话框,选择 允许空提交 选项。
关闭此项目。
点击 项目 → SVN → 结账 查看存储在 SVN 中的项目的所需版本。
点击 项目 → SVN → 断开项目与 SVN 的连接。在随后的对话框中,保留默认选项,然后单击 好吧。
保存该项目。
点击 Git → 初始化 Git 存储库 然后在文件目录中选择一个空文件夹(例如: 温度)。
点击 Git → 提交完成。
保存项目并将其关闭。
这个
*.project
可以删除该项目的文件。Git 存储库Temp
在任何情况下都不得删除该项目。打开
Main Project
再次进行项目。点击 Git → 遥控器 打开 遥控器 观点。
在 遥控器 查看,单击 添加。 添加新遥控器 对话框中,指定 Git 存储库的 URL
Temp
并指定,例如Temp_Remote
作为 别名。选择此添加的遥控器,然后单击 取。
点击 Git → 分支机构 打开 Git 分支 观点。
选择的主分支/主分支
Main
,点击 赛道分支。 追踪远程分支机构 对话框中,选择主分支/主分支Temp_Remote
在步骤 14 中添加的远程。对 Git 存储库的主/主分支执行提取 主要 通过点击 使用期权进行拉动 在 Git 分支 查看并选择 用 “他们的” 来处理冲突 选项作为 合并冲突策略 在 Git Pull 大师 对话框。的默认选项 快进策略 不应更改。
现在点击 Git -→ 提交完成. 在 提交暂存和未暂存的更改 对话框,选择 修改提交 选项。
本指南开头查看的 SVN 项目的修订版现在存储在 Git 存储库中 主要 作为承诺。
在 遥控器 查看,选择
Temp
遥控并点击 移除。保存
Main Project
项目并关闭它。移除 Git 存储库
Temp
从您的文件目录中。如果您想将 SVN 项目的另一个版本转移到 Git 存储库,请按照步骤 6 中的说明进行操作,获取 SVN 项目的下一个所需修订版本。
使用脚本将 SVN 项目转移到 Git
要求: CODESYS Git 版本 >= 1.4.0.0
以下传输脚本模板如下所示:
1。用于从中转移项目的脚本 CODESYS SVN 到 CODESYS Git
2。用于检查传输期间是否为每个 SVN 修订版创建了相应的 Git 提交的脚
未对 SVN 存储库进行任何更改。
这两个脚本都用作模板,您需要对其进行调整以适应相应的要求。
用于传输 SVN 项目的脚本
该脚本传输主干的所有 SVN 修订版 CODESYS SVN 项目到 a 的 “主” 分支 CODESYS Git 项目。
import os import shutil import logging """ --- inputs --- """ # URL of the SVN project you want to migrate # only migrate a single project at a time if you have multiple in your SVN repo SVN_REPO_URL = 'svn://your.svnserver.com/trunk/CodesysProjectNr1' # Starts migration at this svn revision STARTING_REVISION = 0 # Directory of the new project with the git repository NEW_PROJECT_DIR = 'C:/CodesysProjects/CodesysProjectNr1' # Name of the new project NEW_PROJECT_NAME = 'CodesysProjectNr1' # Is the project a library (otherwise will be checked out as a project) IS_LIBRARY = False # Path to the local git repository of the new project NEW_PROJECT_REPO_DIR = 'C:/CodesysProjects/CodesysProjectNr1/repo' # Path to an EMPTY temporary folder (doesn't have to exist) that can be deleted TEMP_PATH = 'C:/CodesysProjects/CodesysProjectNr1/temp' # Author of the git commits AUTHOR_NAME = 'Author Name' # E-mail address of the git commits AUTHOR_EMAIL = 'Author.Name@e-mail.com' # Name of the branch that should contain the history # Can't be named 'master' TARGET_BRANCH_NAME = 'develop' # Initial commit messages on the master branch and the target branch IC_MSG_MASTER = 'master: Initial commit.' IC_MSG_TARGET_BRANCH = 'develop: Initial commit.' # Removes the newly created project and its repository if the migration fails REMOVE_MAIN_PROJECT_ON_FAILURE = True def get_git_commit_msg(svn_log): """ Returns the commit messages that will be seen in the resulting git history Can be customized to ones demands. *svn_log* see: https://content.helpme-codesys.com/en/ScriptingEngine/ScriptSubversion.html#ScriptSubversion.LogInfo """ return 'SVN revision %d: %s' % (svn_log.revision, svn_log.message) """ --- migration script --- """ # Type extension of the project (.project / .library) PROJECT_FILE_TYPE_EXTENSION = '.library' if IS_LIBRARY else '.project' # Path to your new Git project NEW_MAIN_PROJECT_PATH = os.path.join(NEW_PROJECT_DIR, NEW_PROJECT_NAME + PROJECT_FILE_TYPE_EXTENSION) # Name of the temporarily added remote TEMP_REMOTE_NAME = 'remote1' # Temporary commit message will be overwritten/amended (put what u like) TEMP_COMMIT_MSG = 'TEMP COMMIT: This will be overwritten' def create_git_project(): """ 1. Creates a new project at *NEW_MAIN_PROJECT_PATH* 2. Initializes it in git with the repository at *NEW_PROJECT_REPO_DIR* 3. Makes an initial commit with the message *IC_MSG_MASTER* 4. Creates and switches to the target branch *TARGET_BRANCH_NAME* 5. Makes an initial commit on the target branch with the message *IC_MSG_TARGET_BRANCH* 6. Saves and closes the project """ git_project = projects.create(NEW_MAIN_PROJECT_PATH) git_project.git.init(NEW_PROJECT_REPO_DIR) git_project.git.commit_complete(IC_MSG_MASTER, AUTHOR_NAME, AUTHOR_EMAIL) git_project, git_branch = git_project.git.branch_copy(TARGET_BRANCH_NAME, checkout=True) git_project.git.commit_complete(IC_MSG_TARGET_BRANCH, AUTHOR_NAME, AUTHOR_EMAIL, bAllowEmptyCommits=True) git_project.save() git_project.close() def remove_all_children(project): """ Removes all objects that can be removed from the project *project* """ children = project.get_children(recursive=True) for child in children: try: child.remove() except Exception as ex: if not 'Object reference not set to an instance of an object.' == str(ex): logging.error(ex) def create_git_repo_from_svn_revision(svn_log): """ 1. Checks out a project from the SVN server *SVN_REPO_URL* 2. Disconnects it from SVN 3. Initiates it in git with the repository at *repo_dir* 4. Makes a commit with the message *commit_message* 4.1 See "get_git_commit_msg" to customize the commit message 5. Saves and closes the project 6. Returns the path of the project and path of the repository """ project_path = os.path.join(TEMP_PATH, 'revision_' + str(svn_log.revision) + PROJECT_FILE_TYPE_EXTENSION) repo_dir = os.path.join(TEMP_PATH, 'repo_' + str(svn_log.revision)) commit_message = get_git_commit_msg(svn_log) temp_project_name = 'revision_%d' % svn_log.revision svn_project = svn.checkout(SVN_REPO_URL, TEMP_PATH, temp_project_name, svn_log.revision, as_library=IS_LIBRARY) svn_project.svn.disconnect() svn_project.git.init(repo_dir) svn_project.git.commit_complete(commit_message, AUTHOR_NAME, AUTHOR_EMAIL) svn_project.save() svn_project.close() return project_path, repo_dir def magic_git_pull(project, temp_repo_path, svn_log): """ 1. Removes everything from the *project* (to insure svn revision and git commit match exactly) 2. Pulls from the repo at *repo_path* 4. Commits the changes with the commit message *commit_message* 5. Saves and closes the project """ remove_all_children(project) # Temp commit for fake git merge --squash project.git.commit_complete(TEMP_COMMIT_MSG, AUTHOR_NAME, AUTHOR_EMAIL, bAllowEmptyCommits=True) project.git.remote_add(TEMP_REMOTE_NAME, temp_repo_path) project.git.fetch(TEMP_REMOTE_NAME) project.git.branch_track('refs/remotes/%s/master' % TEMP_REMOTE_NAME, TARGET_BRANCH_NAME) pull_options = git.get_pull_options() pull_options.fast_forward_strategy = GitFastForwardStrategy.NoFastForward pull_options.resolve_file_conflict_strategy = GitResolveFileConflictStrategy.Theirs project, merge_result = project.git.pull(AUTHOR_NAME, AUTHOR_EMAIL, pull_options) commit_message = get_git_commit_msg(svn_log) project.git.commit_complete(commit_message, AUTHOR_NAME, AUTHOR_EMAIL, bAllowEmptyCommits=True, bAmendCommit=True) project.git.branch_unset_upstream() project.git.remote_remove(TEMP_REMOTE_NAME) project.save() project.close() def remove_repo(project_path): try: project = projects.open(project_path) project.git.de_init(True) project.close() except: pass def remove_temp_project(project_path): """ Opens the project at *project_path* and deinitializes its git repository This needs to be done because the script can't delete the repository with "shutil.rmtree" Removes the rest of the contents int the folder at *TEMP_PATH* Removes the temporary .project/.library files and their associated files """ remove_repo(project_path) try: shutil.rmtree(TEMP_PATH) os.makedirs(TEMP_PATH) except Exception as e: logging.error('Failed to delete %s. Reason: %s' % (TEMP_PATH, e)) def main(): if projects.primary is not None: projects.primary.close() create_git_project() # get the list of all revision of SVN_REPO_URL # oldest to newest # starting at revision STARTING_REVISION + 1 svn_logs = list(reversed(svn.get_log(SVN_REPO_URL))) svn_logs_to_migrate = [log for log in svn_logs if STARTING_REVISION <= log.revision] assert svn_logs_to_migrate, ('Nothing to migrate. STARTING_REVISION is greater than the newest ' 'revision in %s') % SVN_REPO_URL system.prompt_answers['LossOfDataWarning2'] = PromptResult.Yes no_project_root_dir_amount = 0 try: for svn_log in svn_logs_to_migrate: try: temp_project_path, temp_git_repo_path = create_git_repo_from_svn_revision(svn_log) # pull to your main Git project git_project = projects.open(NEW_MAIN_PROJECT_PATH) magic_git_pull(git_project, temp_git_repo_path, svn_log) # can be omitted if enough storage space is available (big performance increase) # if omitted the folder at "TEMP_PATH" needs to be deleted manually # needed storage space = (project file size + project git repository size) * revisions # example: 255.16 MB = (1.6 KB + 2.55 MB) * 100 remove_temp_project(temp_project_path) except ValueError as ve: # Early svn revisions often do not contain a codesys project if ('The URL %s is not a project root directory.' % SVN_REPO_URL) == str(ve): no_project_root_dir_amount += 1 if no_project_root_dir_amount < len(svn_logs_to_migrate): logging.info(ve) continue logging.critical(ve) raise except: if REMOVE_MAIN_PROJECT_ON_FAILURE: remove_repo(NEW_MAIN_PROJECT_PATH) for file in os.listdir(NEW_PROJECT_DIR): if file.startswith(NEW_PROJECT_NAME): os.remove(os.path.join(NEW_PROJECT_DIR, file)) raise finally: system.prompt_answers.clear() if __name__ == '__main__': main()
检查传输结果的脚本
以下脚本执行以下检查:
检查中是否有相应的 Git 提交 CODESYS Git 每次 SVN 版本的中继版本传输后的项目 CODESYS SVN 项目
检查是否 CODESYS SVN 修订版的项目和 CODESYS 相应 Git 提交的项目是相同的
a 的比较 CODESYS 项目基于 CODESYS进行转移和验证的开发环境。
注意
常用的脚本编写方法 compare_to()
在 CODESYS 在这种情况下表现不如预期。因此,脚本中使用了变通方法。
import os import logging """ --- inputs --- """ # URL of the SVN project you want to check the migration for SVN_REPO_URL = 'svn://your.svnserver.com/trunk/CodesysProjectNr1' # Directory of the project with git repository to check PROJECT_DIR = 'C:/CodesysProjects/CodesysProjectNr1' # Name of the project to check PROJECT_NAME = 'CodesysProjectNr1' # Is the project a library (otherwise will be checked out as a project) IS_LIBRARY = False # Path to an EMPTY temporary folder (doesn't have to exist) that can be deleted TEMP_PATH = 'C:/CodesysProjects/CodesysProjectNr1/temp' # Name of the branch that should be checked TARGET_BRANCH_NAME = 'develop' """ --- check script --- """ # Type extension of the project (.project / .library) PROJECT_FILE_TYPE_EXTENSION = '.library' if IS_LIBRARY else '.project' # Path to your new Git project MAIN_PROJECT_PATH = os.path.join(PROJECT_DIR, PROJECT_NAME + PROJECT_FILE_TYPE_EXTENSION) def get_revision_from_commit_msg(msg): """ filters out the revision number from the commit message *msg* migrated SVN commits in git: git commit = 'SVN revision *revision number*: *svn commit message*' if your migrated svn commits in git are different this possibly won't work """ s_num = '' for c in msg: if c.isdigit(): s_num = s_num + c if ':' == c: break return int(s_num) def compare_changed_objects_workaround(changed_objects): """ Compares the changed objects in the *changed_objects* list by comparing their content Returns True if the objects are identical (not changed) """ for changed_object in changed_objects: if changed_object.left_object is None: return False if changed_object.right_object is None: return False if not changed_object.left_object.textual_declaration.text == changed_object.right_object.textual_declaration.text: logging.error('Compared objects are not the same:') logging.error('left: %s/%s' % (changed_object.left_object.parent.get_name(), changed_object.left_object.get_name())) logging.error(changed_object.left_object.textual_declaration.text) logging.error('right: %s/%s' % (changed_object.right_object.parent.get_name(), changed_object.right_object.get_name())) logging.error(changed_object.right_object.textual_declaration.text) logging.error('diff: ' + str(changed_object.differences)) logging.error('-----------------------------------------------------') return False return True def compare_projects(project_one, project_two): """ Compares two projects Returns True if they are identical """ comparison_result = project_one.compare_to(project_two, flags=ComparisonFlags.IGNORE_PROPERTIES) changed_objects = list(comparison_result.get_changed_objects()) if 0 == len(changed_objects): return True else: return compare_changed_objects_workaround(changed_objects) def main(): system.prompt_handling = PromptHandling.LogSimplePrompts if projects.primary is not None: projects.primary.close() git_project = projects.open(MAIN_PROJECT_PATH) git_project, git_branch = git_project.git.checkout(TARGET_BRANCH_NAME, force=True) git_logs = list(reversed(list(git_project.git.log()))) system.prompt_answers['LossOfDataWarning2'] = PromptResult.Yes checks_done = 0 try: for git_log in git_logs: if 'SVN revision ' in git_log.message_string: git_project, git_null_branch = git_project.git.checkout(git_log.sha_string) revision = get_revision_from_commit_msg(git_log.message_string) temp_project = svn.checkout(SVN_REPO_URL, TEMP_PATH, 'revision_' + str(revision), revision, as_library=IS_LIBRARY ,as_primary_project=False) if not compare_projects(git_project, temp_project): prompt_message = ("Revision %d does not equal it's corresponding git commit. " "Do you want to continue?") % revision logging.error(prompt_message) prompt_result = system.ui.prompt(prompt_message, PromptChoice.YesNo, PromptResult.No) if PromptResult.No == prompt_result: temp_project.close() break checks_done += 1 temp_project.close() finally: system.prompt_answers.clear() git_project, git_branch = git_project.git.checkout(TARGET_BRANCH_NAME) git_project.close() if checks_done: system.ui.prompt('Done', PromptChoice.OK, PromptResult.OK) else: error_msg = 'No migrated commits found. If you have custom commit messages change this script accordingly.' logging.error(error_msg) raise Exception(error_msg) if __name__ == '__main__': main()