Skip to main content

Mejores prácticas

Restaurar un proyecto de Git cuando CODESYS se ha bloqueado al editar el proyecto

Según qué operación CODESYS se bloqueó anteriormente, tiene las siguientes opciones de recuperación:

  • Normalmente, puede volver a abrir el proyecto después de un bloqueo. CODESYS Git restaura el último estado del proyecto guardado internamente y vuelve a escribir el contenido del proyecto en el repositorio de proyectos de Git. Este mecanismo de "almacenamiento fallido" trata el proyecto como una fuente de datos "maestra". Si el proyecto falla durante un proceso de fusión, no debe guardar inmediatamente después de volver a abrir antes de continuar trabajando, sino descartar todos los cambios primero (git reset --hard). Puedes hacer esto en el Ver estado y reservas El comando Descartar todos los cambios usar.

  • En caso de que ya no sea posible abrir con éxito el proyecto después del bloqueo (por ejemplo, porque archivos completamente incompatibles terminaron en el almacenamiento del proyecto), tiene la siguiente opción para una recuperación: Vuelva a cargar completamente el proyecto desde el repositorio. Para más información, ver: Reconstruir el proyecto desde el repositorio

Transferir un proyecto desde CODESYS SVN a CODESYS Git

Le recomendamos encarecidamente que no elimine los repositorios SVN después de transferir con éxito los proyectos a GIT para poder acceder a ellos si es necesario.

Sugerencia

En CODESYS Git la versión 1.4.0.0 y posteriores, estará disponible una interfaz de secuencias de comandos para transferir un proyecto desde CODESYS SVN a CODESYS Git.

Importante

El comando Git git-svn no se puede usar para transferir CODESYSproyectos de CODESYS SVN para CODESYS Git porque CODESYS Git usa el .json formato de archivo, a diferencia del formato de archivo binario de CODESYS SVN.

. Comentarios y recomendaciones debido a las diferentes estructuras de repositorio de CODESYS SVN y CODESYS Git
  • En CODESYS Git, un repositorio de Git solo puede administrar uno CODESYS proyecto. Porque un repositorio SVN puede contener varios CODESYS proyectos, se debe crear un repositorio Git independiente para cada proyecto de un repositorio SVN al transferirlo a CODESYS Git.

  • Se recomienda transferir los proyectos individuales de un repositorio SVN a los repositorios de CODESYS Git proyecto por proyecto.

  • No se recomienda dividir un repositorio SVN en varios repositorios SVN porque esto puede causar problemas más adelante.

  • Las ramas y las etiquetas existen tanto en SVN como en GIT, pero están diseñadas de forma diferente en estos dos sistemas. Una transferencia con el git-svn el comando no es posible porque este comando no se puede usar en CODESYS Git (véase más arriba).

    Por lo tanto, nos limitamos a transferir el tronco SVN a una rama de Git (por defecto «Master» o «Main»). Si es necesario, las etiquetas SVN se pueden añadir manualmente como etiquetas de Git

En este procedimiento, la versión más reciente del CODESYS el proyecto se archiva por primera vez en CODESYS SVN. Luego se desconecta la conexión a SVN y se abre un repositorio de Git CODESYS Git se inicializa para el proyecto. La desventaja de este procedimiento es que se pierde el historial del proyecto porque solo se transfiere a Git la última revisión de SVN

Requerimientos: CODESYS SVN y CODESYS Git están instalados en CODESYS. Mediante el HerramientasPersonaliza comando, el Compromiso completado se ha agregado el comando al Integración con Git categoría de comando en Git menú.

  1. Abra el CODESYS proyecto que ha guardado en el repositorio SVN.

  2. Haga clic ProyectoSVNFinalizar compra.

    Se ha revisado la última revisión del proyecto.

  3. Haga clic ProyectoSVNDesconectar el proyecto de SVN.

  4. Cree una nueva carpeta vacía en el directorio de archivos local de su ordenador.

  5. En CODESYS, haga clic GitInicializar el repositorio de Git y selecciona la carpeta vacía creada en el paso anterior del cuadro de diálogo.

  6. Haga clic GitCompromiso completo.

    El proyecto ahora está almacenado en el repositorio local de Git y se puede enviar a un repositorio remoto si es necesario.

En este procedimiento, cada revisión de SVN se transfiere manualmente en una confirmación de Git.

Requerimientos: CODESYS SVN y CODESYS Git están instalados en CODESYS. Mediante el HerramientasPersonaliza comando, el Compromiso completado se ha agregado el comando al Integración con Git categoría de comando en Git menú.

  1. Crea un nuevo proyecto vacío en CODESYS y asígnele un nombre, por ejemplo, Main Project.

  2. Haga clic GitInicializar el repositorio de Git.

  3. Haga clic GitCompromiso completo.

  4. Haga clic GitComprometerse para ejecutar una confirmación vacía. En el Comprometa cambios por etapas cuadro de diálogo, seleccione Permitir confirmación vacía opción.

  5. Cierre este proyecto.

  6. Haga clic ProyectoSVNFinalizar compra para ver la revisión deseada del proyecto almacenada en SVN.

  7. Haga clic ProyectoSVNDesconectar el proyecto de SVN. En el cuadro de diálogo siguiente, mantenga la opción predeterminada y haga clic OK.

  8. Guarda el proyecto.

  9. Haga clic GitInicializar el repositorio de Git y selecciona una carpeta vacía en el directorio de archivos (ejemplo: Temperatura).

  10. Haga clic GitCompromiso completo.

  11. Guarde el proyecto y ciérrelo.

    El *.project puede eliminar el archivo de este proyecto. El repositorio de Git Temp del proyecto no debe borrarse bajo ninguna circunstancia.

  12. Abra el Main Project proyecto de nuevo.

  13. Haga clic GitControles remotos para abrir el Mandos a distancia ver.

  14. En el Controles remotos ver, hacer clic Agregar. En el Añadir nuevo mando a distancia cuadro de diálogo, especifique la URL del repositorio de Git Temp y especificar, por ejemplo, Temp_Remote como el Nombre de alias.

  15. Seleccione este mando a distancia agregado y haga clic Recuperar.

  16. Haga clic GitSucursales para abrir el Ramas de Git vista.

  17. Seleccione la rama maestro/principal de Main, haga clic Rastrear sucursal. En el Rastrea una sucursal remota cuadro de diálogo, seleccione la rama maestro/principal del Temp_Remote remoto que se agregó en el paso 14.

  18. Ejecuta una extracción en la rama maestro/principal del repositorio de Git Principal haciendo clic Tire con opciones en el Ramas de Git ver y seleccionar el Usa «de ellos» para los conflictos opción como Fusionar la estrategia de conflictos en el Git Pull Master cuadro de diálogo. La opción predeterminada para Estrategia de avance rápido no debe cambiarse.

  19. Ahora haga clic Git -→ Compromiso completo. en el Realice cambios por etapas y no por etapas cuadro de diálogo, seleccione el Modificar la confirmación opción.

    La revisión del proyecto SVN que se revisó al principio de esta guía ahora está almacenada en el repositorio de Git. Principal como compromiso.

  20. En el Controles remotos vea, seleccione el Temp remoto y haga clic Eliminar.

  21. Guarde el Main Project proyecta y ciérralo.

  22. Eliminar el repositorio de Git Temp desde tu directorio de archivos.

  23. Si quieres transferir otra revisión del proyecto SVN al repositorio de Git, sigue estas instrucciones del paso 6 para la siguiente revisión que desees del proyecto SVN.

Transferir un proyecto SVN a Git con secuencias de comandos

Requisito: CODESYS Git versión >= 1.4.0.0

Las siguientes plantillas de script para la transferencia se muestran a continuación:

1. Guión para transferir un proyecto desde CODESYS SVN para CODESYS Git

2. Secuencia de comandos para comprobar si se ha creado o no una confirmación de Git correspondiente para cada revisión de SVN durante la transferencia

. Notas
  • No se realizan cambios en el repositorio SVN.

  • Ambos scripts están pensados como plantillas que debe adaptar a los requisitos respectivos.

Guión para la transferencia de un proyecto SVN

El script transfiere todas las revisiones SVN del enlace troncal de un CODESYS SVN proyecto a la rama «maestra» de un CODESYS Git proyecto.

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

Secuencia de comandos para comprobar el resultado de la transferencia

El siguiente script realiza las siguientes comprobaciones:

  1. Comprueba si hay o no una confirmación de Git correspondiente en el CODESYS Git proyecto después de la transferencia para cada revisión SVN del enlace troncal de un CODESYS SVN proyecto

  2. Compruebe si el CODESYS proyecto de una revisión de SVN y el CODESYS el proyecto de la confirmación de Git correspondiente son idénticos

La comparación de un CODESYS el proyecto se basa en la CODESYSentorno de desarrollo donde se realizan la transferencia y la verificación.

Nota

El método habitual de creación de scripts compare_to() en CODESYS no se comporta como se esperaba en este contexto. Por este motivo, se utiliza una solución alternativa en el script

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