Skip to main content

Procedure consigliate

Ripristino di un progetto Git quando CODESYS si è bloccato durante la modifica del progetto

A seconda dell'operazione CODESYS si è bloccato in precedenza, sono disponibili le seguenti opzioni per il ripristino:

  • Normalmente è possibile riaprire il progetto dopo un arresto anomalo. CODESYS Git ripristina l'ultimo stato del progetto salvato internamente e riscrive il contenuto del progetto nell'archivio del progetto Git. Questo meccanismo di «failsave» considera il progetto come una fonte di dati «master». Se il progetto si è bloccato durante un'operazione di unione, non dovresti salvare immediatamente prima di continuare a lavorare dopo la riapertura, ma prima scartare tutte le modifiche (git reset --hard). Per fare ciò, puoi usare Annulla tutte le modifiche comando in Stato e messa in scena vista.

  • Nel caso in cui non sia più possibile aprire correttamente il progetto dopo il crash (ad esempio perché nella memoria del progetto sono finiti file completamente incompatibili), hai la seguente possibilità di ripristino: Ricaricare completamente il progetto dal repository. Per ulteriori informazioni, vedere: Ricostruisci il progetto dal repository

Trasferimento di un progetto da CODESYS SVN a CODESYS Git

Ti consigliamo vivamente di non eliminare i repository SVN dopo aver trasferito con successo i progetti su GIT in modo da poter comunque accedervi se necessario.

Suggerimento

Nel CODESYS Git nella versione 1.4.0.0 e successive, sarà disponibile un'interfaccia di scripting per il trasferimento di un progetto da CODESYS SVN a CODESYS Git.

Importante

Il comando Git git-svn non può essere utilizzato per il trasferimento CODESYSprogetti da CODESYS SVN a CODESYS Git perché CODESYS Git utilizza il .json formato di file, a differenza del formato di file binario di CODESYS SVN.

. Commenti e raccomandazioni dovuti alle diverse strutture di repository di CODESYS SVN e CODESYS Git
  • Nel CODESYS Git, un repository Git può gestirne solo uno CODESYS progetto. Perché un repository SVN può contenere CODESYS progetti, è necessario creare un repository Git separato per ogni progetto in un repository SVN durante il trasferimento a CODESYS Git.

  • Si consiglia di trasferire i singoli progetti di un repository SVN nei repository di CODESYS Git progetto per progetto.

  • Non è consigliabile suddividere un repository SVN in più repository SVN perché ciò può causare problemi in seguito.

  • I rami e i tag esistono sia in SVN che in GIT, ma sono progettati in modo diverso in questi due sistemi. Un trasferimento con git-svn il comando non è possibile perché questo comando non può essere utilizzato in CODESYS Git (vedi sopra).

    Pertanto, ci limitiamo a trasferire il trunk SVN su un ramo Git (di default «Master» o «Main»). Se necessario, i tag SVN possono essere aggiunti manualmente come tag

In questa procedura, l'ultima versione di CODESYS il progetto viene prima archiviato CODESYS SVN. Quindi la connessione a SVN viene disconnessa e viene creato un repository Git CODESYS Git viene inizializzato per il progetto. Lo svantaggio di questa procedura è che la cronologia del progetto viene persa perché solo l'ultima revisione SVN viene

Requisiti: CODESYS SVN e CODESYS Git sono installati in CODESYS. Per mezzo del UtensiliPersonalizza comando, Commit completo il comando è stato aggiunto a Integrazione con Git categoria di comando in Git menu.

  1. Aprire il CODESYS progetto che avete salvato nel repository SVN.

  2. Clicca ProgettoSVNPagamento.

    L'ultima revisione del progetto è stata sottoposta a verifica.

  3. Clicca ProgettoSVNDisconnetti il progetto da SVN.

  4. Crea una nuova cartella vuota nella directory locale dei file del tuo computer.

  5. Nel CODESYS, fai clic RegaloInizializza il repository Git e seleziona la cartella vuota creata nel passaggio precedente nella finestra di dialogo.

  6. Clicca GitImpegno completo.

    Il progetto è ora archiviato nel repository Git locale e può essere inviato a un repository remoto, se necessario.

In questa procedura, ogni singola revisione SVN viene trasferita manualmente in un commit Git.

Requisiti: CODESYS SVN e CODESYS Git sono installati in CODESYS. Per mezzo del UtensiliPersonalizza comando, Commit completo il comando è stato aggiunto a Integrazione con Git categoria di comando in Git menu.

  1. Crea un nuovo progetto vuoto in CODESYS e assegnagli un nome, ad esempio Main Project.

  2. Clicca GitInizializza il repository Git.

  3. Clicca GitImpegno completo.

  4. Clicca GitCommettere per eseguire un commit vuoto. Effettua modifiche graduali finestra di dialogo, seleziona Consenti un commit vuoto opzione.

  5. Chiudi questo progetto.

  6. Clicca ProgettoSVNPagamento per verificare la revisione desiderata del progetto archiviata in SVN.

  7. Clicca ProgettoSVNDisconnetti il progetto da SVN. Nella finestra di dialogo successiva, mantieni l'opzione predefinita e fai clic OK.

  8. Salva il progetto.

  9. Clicca GitInizializza il repository Git e seleziona una cartella vuota nella directory dei file (esempio: Temperatura).

  10. Clicca GitImpegno completo.

  11. Salvate il progetto e chiudetelo.

    Le *.project il file di questo progetto può essere eliminato. Il repository Git Temp del progetto non deve essere cancellato in nessun caso.

  12. Aprire il Main Project di nuovo progetto.

  13. Clicca GitTelecomandi per aprire il Telecomandi vista.

  14. Nel Telecomandi visualizza, fai clic Aggiungi. Aggiungi un nuovo telecomando finestra di dialogo, specifica l'URL del repository Git Temp e specificare, ad esempio, Temp_Remote era il Nome alias.

  15. Seleziona questo telecomando aggiunto e fai clic su Recupera.

  16. Clicca GitFiliali per aprire il Filiali Git vista.

  17. Seleziona il master/ramo principale di Main, fai clic Tieni traccia della filiale. Tieni traccia della filiale remota finestra di dialogo, seleziona il ramo master/principale del Temp_Remote telecomando che è stato aggiunto nel passaggio 14.

  18. Esegui un pull sul ramo master/main del repository Git Principale facendo clic Estrai con opzioni nel Filiali Git visualizza e seleziona Usa «loro» per i conflitti opzione come Strategia di unione dei conflitti nel Git Pull Master dialogo. L'opzione predefinita per Strategia Fast Forward non dovrebbe essere cambiato.

  19. Ora fai clic Git -→ Impegno completo. nel Effettua modifiche graduali e non graduali finestra di dialogo, seleziona il Modifica commit opzione.

    La revisione del progetto SVN che è stata verificata all'inizio di questa guida è ora archiviata nel repository Git Principale come impegno.

  20. Nel Telecomandi visualizza, seleziona Temp remoto e fai clic Rimuovi.

  21. Salva il Main Project progetto e chiudilo.

  22. Rimuovi il repository Git Temp dalla tua cartella dei file.

  23. Se desideri trasferire un'altra revisione del progetto SVN nel repository Git, segui queste istruzioni del passaggio 6 per la successiva revisione desiderata del progetto SVN.

Trasferimento di un progetto SVN su Git con script

Requisito: CODESYS Git versione >= 1.4.0.0

Di seguito sono visualizzati i seguenti modelli di script per il trasferimento:

1. Script per il trasferimento di un progetto CODESYS SVN a CODESYS Git

2. Script per verificare se è stato creato o meno un commit Git corrispondente per ogni revisione SVN durante il trasferimento

. Note
  • Non viene apportata alcuna modifica al repository SVN.

  • Entrambi gli script sono intesi come modelli che è necessario adattare ai rispettivi requisiti.

Script per il trasferimento di un progetto SVN

Lo script trasferisce tutte le revisioni SVN del trunk di un CODESYS SVN progetto al ramo «master» di a CODESYS Git progetto.

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()

Script per la verifica del risultato del trasferimento

Il seguente script esegue i seguenti controlli:

  1. Controlla se c'è o meno un commit Git corrispondente nel CODESYS Git progetto dopo il trasferimento per ogni revisione SVN del trunk di a CODESYS SVN progetto

  2. Controlla se il CODESYS progetto di una revisione SVN e il CODESYS il progetto del commit Git corrispondente sono identici

Il confronto di un CODESYS il progetto si basa sul CODESYSambiente di sviluppo in cui vengono eseguiti il trasferimento e la verifica.

Nota

Il solito metodo di scripting compare_to() nel CODESYS non si comporta come previsto in questo contesto. Per questo motivo, nello script viene utilizzata una soluzione alternativa

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()