ベスト・プラクティス
以下の場合に Git プロジェクトを復元する CODESYS プロジェクトの編集中にクラッシュした
操作によって異なる CODESYS 以前にクラッシュしてしまった場合は、次の方法で復旧できます。
通常、クラッシュ後にプロジェクトを再度開くことができます。 CODESYS Git 内部に保存された最後のプロジェクトの状態を復元し、プロジェクトの内容を Git プロジェクトストレージに書き換えます。この「フェイルセーブ」メカニズムは、プロジェクトを「マスター」データソースとして扱います。マージ操作中にプロジェクトがクラッシュした場合は、再度開いた後に作業を続ける直前に保存せず、まずすべての変更を破棄してください (git reset--hard)。そのためには、以下を使用できます すべての変更を破棄する のコマンド ステータスとステージング 表示。
クラッシュ後にプロジェクトを正常に開くことができなくなった場合 (たとえば、完全に互換性のないファイルがプロジェクト ストレージに残ってしまったため)、次のリカバリ オプションがあります。 リポジトリからプロジェクトを完全に再ロードします。詳細については、以下を参照してください。 リポジトリからプロジェクトを再構築
からのプロジェクトの転送 CODESYS SVN へ CODESYS Git
プロジェクトを GIT に正常に転送した後は、必要に応じて引き続き SVN リポジトリにアクセスできるように、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 GitGit リポジトリが管理できるのは 1 つだけです。 CODESYS プロジェクト。SVN リポジトリには複数を含めることができるからです CODESYS プロジェクトを SVN リポジトリに転送するときは、SVN リポジトリ内のプロジェクトごとに個別の Git リポジトリを作成する必要があります。 CODESYS Git。
SVN リポジトリの個々のプロジェクトを、次のリポジトリに転送することをお勧めします。 CODESYS Git プロジェクト単位で。
SVN リポジトリを複数の SVN リポジトリに分割すると、後で問題が発生する可能性があるため、お勧めしません。
ブランチとタグは SVN と GIT の両方に存在しますが、これらの 2 つのシステムでは設計が異なります。との移管です
git-svn
このコマンドは次の場所では使用できないため、コマンドは使用できません。 CODESYS Git (上記参照)。そのため、SVN トランクを Git ブランチ (デフォルトでは「Master」または「Main」) に転送することに限定しています。必要に応じて 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 リビジョンが 1 回の Git コミットで手動で転送されます。
必要条件: CODESYS SVN と CODESYS Git にインストールされています CODESYS。 ツール → カスタマイズ コマンド、 コミット完了 コマンドがに追加されました Git インテグレーション のコマンドカテゴリ Git メニュー。
に新しい空のプロジェクトを作成 CODESYS そして、例えば以下のように名前を付けます。
Main Project
。クリック Git → Git リポジトリを初期化。
クリック Git → コミット完了。
クリック Git → コミット 空のコミットを実行する。 段階的な変更をコミットする ダイアログで [] を選択します。 空のコミットを許可 オプション。
このプロジェクトを閉じます。
クリック [プロジェクト] → SVN → チェックアウト SVN に保存されているプロジェクトの希望のリビジョンをチェックアウトします。
クリック [プロジェクト] → SVN → プロジェクトを SVN から切断。表示されるダイアログでは、デフォルトのオプションをそのまま使用してをクリックします OK。
プロジェクトを保存します。
クリック Git → Git リポジトリを初期化 そして、ファイルディレクトリ内の空のフォルダを選択します (例: Temp)。
クリック Git → コミット完了。
プロジェクトを保存して閉じます。
ザ・
*.project
このプロジェクトのファイルは削除できます。Git リポジトリTemp
プロジェクトの削除はいかなる場合でも行わないでください。を開きます
Main Project
プロジェクト再び。クリック Git → リモコン を開くには リモコン 見る。
の中に リモコン 表示、クリック [追加]。 新しいリモコンを追加 ダイアログで Git リポジトリの URL を指定
Temp
そして、たとえば次のように指定します。Temp_Remote
として エイリアス名。この追加されたリモコンを選択してクリック フェッチ。
クリック Git → ブランチ を開くには Git ブランチ 表示。
のマスター/メインブランチを選択してください
Main
、クリック トラックブランチ。 リモートブランチを追跡 ダイアログで、のマスター/メインブランチを選択しますTemp_Remote
ステップ 14 で追加したリモート。Git リポジトリのマスター/メインブランチでプルを実行する メイン クリックして オプション付きプル で Git ブランチ の表示と選択 コンフリクトには「相手のもの」を使う オプションとしては マージコンフリクトストラテジー で Git プルマスター ダイアログ。のデフォルトオプション 早送り戦略 変更すべきではありません。
今すぐクリック 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 コミットのプロジェクトは同一です
の比較 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()