*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_DEBUG
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_debuginfo DEFINITION FINAL INHERITING FROM lcl_gui_page.
  PUBLIC SECTION.
    METHODS constructor.

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts        REDEFINITION.

  PRIVATE SECTION.
    METHODS render_debug_info
      RETURNING VALUE(ro_html) TYPE REF TO lcl_html.
    METHODS render_supported_object_types
      RETURNING VALUE(rv_html) TYPE string.

ENDCLASS.                       "lcl_gui_page_debuginfo

CLASS lcl_gui_page_debuginfo IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'DEBUG INFO'.
  ENDMETHOD.  " constructor.

  METHOD render_content.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( render_debug_info( ) ).
    ro_html->add( render_supported_object_types( ) ).
    ro_html->add( '
' ).

  ENDMETHOD.  "render_content

  METHOD render_debug_info.

    DATA: lt_ver_tab     TYPE filetable,
          lv_rc          TYPE i,
          lv_gui_version TYPE string,
          ls_version     LIKE LINE OF lt_ver_tab.

    cl_gui_frontend_services=>get_gui_version(
      CHANGING version_table = lt_ver_tab rc = lv_rc
      EXCEPTIONS OTHERS = 1 ).
    READ TABLE lt_ver_tab INTO ls_version INDEX 1.
    lv_gui_version = ls_version-filename.
    READ TABLE lt_ver_tab INTO ls_version INDEX 2.
    lv_gui_version = |{ lv_gui_version }.{ ls_version-filename }|.

    CREATE OBJECT ro_html.

    ro_html->add( |
abapGit version: { gc_abap_version }
| ).
    ro_html->add( |
XML version:     { gc_xml_version }
| ).
    ro_html->add( |
GUI version:     { lv_gui_version }
| ).

  ENDMETHOD. "render_debug_info

  METHOD render_supported_object_types.

    DATA: lt_objects TYPE STANDARD TABLE OF ko100,
          lv_list    TYPE string,
          ls_item    TYPE ty_item.

    FIELD-SYMBOLS <object> LIKE LINE OF lt_objects.

    CALL FUNCTION 'TR_OBJECT_TABLE'
      TABLES
        wt_object_text = lt_objects
      EXCEPTIONS
        OTHERS         = 1 ##FM_SUBRC_OK.

    LOOP AT lt_objects ASSIGNING <object> WHERE pgmid = 'R3TR'.
      ls_item-obj_type = <object>-object.
      IF lcl_objects=>is_supported( is_item = ls_item iv_native_only = abap_true ) = abap_true.
        IF lv_list IS INITIAL.
          lv_list = ls_item-obj_type.
        ELSE.
          lv_list = lv_list && `, ` && ls_item-obj_type.
        ENDIF.
      ENDIF.
    ENDLOOP.

    rv_html = |
Supported objects: { lv_list }
|.

  ENDMETHOD.  " render_supported_object_types

  METHOD scripts.

    CREATE OBJECT ro_html.

    ro_html->add( 'debugOutput("Browser: " + navigator.userAgent, "debug_info");' ).

  ENDMETHOD.  "scripts

ENDCLASS.                       "lcl_gui_page_debuginfo

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_DEBUG
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_debuginfo DEFINITION FINAL INHERITING FROM lcl_gui_page.
  PUBLIC SECTION.
    METHODS constructor.

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts        REDEFINITION.

  PRIVATE SECTION.
    METHODS render_debug_info
      RETURNING VALUE(ro_html) TYPE REF TO lcl_html
      RAISING lcx_exception.
    METHODS render_supported_object_types
      RETURNING VALUE(rv_html) TYPE string.

ENDCLASS.                       "lcl_gui_page_debuginfo

CLASS lcl_gui_page_debuginfo IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'DEBUG INFO'.
  ENDMETHOD.  " constructor.

  METHOD render_content.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( render_debug_info( ) ).
    ro_html->add( render_supported_object_types( ) ).
    ro_html->add( '
' ).

  ENDMETHOD.  "render_content

  METHOD render_debug_info.

    DATA: lt_ver_tab     TYPE filetable,
          lv_rc          TYPE i,
          lv_gui_version TYPE string,
          ls_version     LIKE LINE OF lt_ver_tab.

    cl_gui_frontend_services=>get_gui_version(
      CHANGING version_table = lt_ver_tab rc = lv_rc
      EXCEPTIONS OTHERS = 1 ).
    READ TABLE lt_ver_tab INTO ls_version INDEX 1.
    lv_gui_version = ls_version-filename.
    READ TABLE lt_ver_tab INTO ls_version INDEX 2.
    lv_gui_version = |{ lv_gui_version }.{ ls_version-filename }|.

    CREATE OBJECT ro_html.

    ro_html->add( |
abapGit version: { gc_abap_version }
| ).
    ro_html->add( |
XML version:     { gc_xml_version }
| ).
    ro_html->add( |
GUI version:     { lv_gui_version }
| ).
    ro_html->add( |
LCL_TIME:        { lcl_time=>get( ) }
| ).
    ro_html->add( |
SY time:         { sy-datum } { sy-uzeit } { sy-tzone }
| ).

  ENDMETHOD. "render_debug_info

  METHOD render_supported_object_types.

    DATA: lt_objects TYPE STANDARD TABLE OF ko100,
          lv_list    TYPE string,
          ls_item    TYPE ty_item.

    FIELD-SYMBOLS <object> LIKE LINE OF lt_objects.

    CALL FUNCTION 'TR_OBJECT_TABLE'
      TABLES
        wt_object_text = lt_objects
      EXCEPTIONS
        OTHERS         = 1 ##FM_SUBRC_OK.

    LOOP AT lt_objects ASSIGNING <object> WHERE pgmid = 'R3TR'.
      ls_item-obj_type = <object>-object.
      IF lcl_objects=>is_supported( is_item = ls_item iv_native_only = abap_true ) = abap_true.
        IF lv_list IS INITIAL.
          lv_list = ls_item-obj_type.
        ELSE.
          lv_list = lv_list && `, ` && ls_item-obj_type.
        ENDIF.
      ENDIF.
    ENDLOOP.

    rv_html = |
Supported objects: { lv_list }
|.

  ENDMETHOD.  " render_supported_object_types

  METHOD scripts.

    CREATE OBJECT ro_html.

    ro_html->add( 'debugOutput("Browser: " + navigator.userAgent + "
Frontend time: " + new Date(), "debug_info");' ).

  ENDMETHOD.  "scripts

ENDCLASS.                       "lcl_gui_page_debuginfo

debug page: add time #576

debug page: add time #576


*&---------------------------------------------------------------------*
*&  Include  zabapgit_object_prag
*&---------------------------------------------------------------------*

CLASS lcl_object_prag DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.

ENDCLASS.

CLASS lcl_object_prag IMPLEMENTATION.

  METHOD lif_object~changed_by.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~compare_to_remote_version.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~delete.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~deserialize.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~exists.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~get_metadata.

    rs_metadata = get_metadata( ).
    rs_metadata-delete_tadir = abap_true.

  ENDMETHOD.

  METHOD lif_object~has_changed_since.

    rv_changed = abap_true.

  ENDMETHOD.

  METHOD lif_object~jump.

    call function 'RS_TOOL_ACCESS'
      EXPORTING
        operation           = 'SHOW'    " Operation
        object_name         = ms_item-obj_name    " Object Name
        object_type         = ms_item-obj_type    " Object Type
*        enclosing_object    =     " Program Name
*        position            = SPACE
*        devclass            =
*        include             =
*        version             = SPACE    " 'A': Active, 'I': Inactive, SPACE: Corresponds to Work List
*        monitor_activation  = 'X'
*        wb_manager          =
*        in_new_window       =
*        with_objectlist     = SPACE
*        with_worklist       = SPACE
*      IMPORTING
*        new_name            =     " New Program Name (After Rename)
*        wb_todo_request     =
*      TABLES
*        objlist             =
*      CHANGING
*        p_request           = SPACE    " Request/Task
*      EXCEPTIONS
*        not_executed        = 1
*        invalid_object_type = 2
*        others              = 3
      .
    IF sy-subrc <> 0.
*     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
*                WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.

*    DATA: object_data TYPE REF TO if_wb_object_data_model.
*
*    DATA(persist) = NEW cl_wb_abpr_persist( ).
*
*    TRY.
*        persist->if_wb_object_persist~get(
*          EXPORTING
*            p_object_key                 = |{ ms_item-obj_name }|     " Object Key
*            p_version                    = 'A'    " Version (Active/Inactive)
**        p_language                   = SY-LANGU    " Language Key (Important: must not trigger exception)
**        p_if_none_match              =
**        p_existence_check_only       = ABAP_FALSE    " Perform Existence Check Only (no existence -> exception)
**        p_data_selection             = C_ALL_DATA    " Selection (or Filter) of Object Data
**        p_wb_rest                    =
**      IMPORTING
**        p_langu_is_not_maintained    =     " 'X': No data exists in specified language
**        p_etag                       =
**        p_other_existing_versions    =
*      CHANGING
*        p_object_data                = object_data
*        ).
*
*      CATCH cx_swb_object_does_not_exist cx_swb_exception INTO DATA(error).
*        MESSAGE error TYPE 'S' DISPLAY LIKE 'E'.
*        RETURN.
*    ENDTRY.
*
*    DATA(ui) = NEW cl_wb_abpr_ui( ).
*
*    DATA(tool) = NEW cl_wb_abpr_tool(
*        p_ui      = ui
*        p_persist = persist
*    ).
*
*    DATA(tool_data) = NEW cl_wb_generic_tool_data( ).
*
*    tool_data->if_wb_tool_data_model~set_edit_mode( 'EDIT' ).
*
*    ui->if_wb_tool_ui~set_data(
*      EXPORTING
*        p_object_data = object_data     " Object Data
*        p_tool_data   = tool_data
*    ).
*
*    ui->if_wb_tool_ui~start(
*      EXPORTING
*        p_tool_ref     = tool    " Reference to WB Tool
*      EXCEPTIONS
*        error_occurred = 1
*        OTHERS         = 2 ).

  ENDMETHOD.

  METHOD lif_object~serialize.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

ENDCLASS.

*&---------------------------------------------------------------------*
*&  Include  zabapgit_object_prag
*&---------------------------------------------------------------------*

CLASS lcl_object_prag DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.

ENDCLASS.

CLASS lcl_object_prag IMPLEMENTATION.

  METHOD lif_object~changed_by.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~compare_to_remote_version.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~delete.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~deserialize.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

  METHOD lif_object~exists.

    DATA(persist) = NEW cl_wb_abpr_persist( ).

    TRY.
        persist->if_wb_object_persist~get( p_object_key 					= |{ ms_item-obj_name }|     " Object Key
                                           p_version							= 'A'    " Version (Active/Inactive)
                                           p_existence_check_only = abap_true     " Perform Existence Check Only (no existence -> exception)
        ).

      CATCH cx_swb_exception INTO DATA(error).
        rv_bool = abap_false.
        RETURN.
    ENDTRY.

    rv_bool = abap_true.

*    DATA: object_data TYPE REF TO if_wb_object_data_model.
*
*    DATA(persist) = NEW cl_wb_abpr_persist( ).
*
*    TRY.
*        persist->if_wb_object_persist~get(
*          EXPORTING
*            p_object_key                 = |{ ms_item-obj_name }|     " Object Key
*            p_version                    = 'A'    " Version (Active/Inactive)
**        p_language                   = SY-LANGU    " Language Key (Important: must not trigger exception)
**        p_if_none_match              =
**        p_existence_check_only       = ABAP_FALSE    " Perform Existence Check Only (no existence -> exception)
**        p_data_selection             = C_ALL_DATA    " Selection (or Filter) of Object Data
**        p_wb_rest                    =
**      IMPORTING
**        p_langu_is_not_maintained    =     " 'X': No data exists in specified language
**        p_etag                       =
**        p_other_existing_versions    =
*      CHANGING
*        p_object_data                = object_data
*        ).
*
*      CATCH cx_swb_object_does_not_exist cx_swb_exception INTO DATA(error).
*        MESSAGE error TYPE 'S' DISPLAY LIKE 'E'.
*        RETURN.
*    ENDTRY.
*
*    DATA(ui) = NEW cl_wb_abpr_ui( ).
*
*    DATA(tool) = NEW cl_wb_abpr_tool(
*        p_ui      = ui
*        p_persist = persist
*    ).
*
*    DATA(tool_data) = NEW cl_wb_generic_tool_data( ).
*
*    tool_data->if_wb_tool_data_model~set_edit_mode( 'EDIT' ).
*
*    ui->if_wb_tool_ui~set_data(
*      EXPORTING
*        p_object_data = object_data     " Object Data
*        p_tool_data   = tool_data
*    ).
*
*    ui->if_wb_tool_ui~start(
*      EXPORTING
*        p_tool_ref     = tool    " Reference to WB Tool
*      EXCEPTIONS
*        error_occurred = 1
*        OTHERS         = 2 ).

  ENDMETHOD.

  METHOD lif_object~get_metadata.

    rs_metadata = get_metadata( ).
    rs_metadata-delete_tadir = abap_true.

  ENDMETHOD.

  METHOD lif_object~has_changed_since.

    rv_changed = abap_true.

  ENDMETHOD.

  METHOD lif_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation           = 'SHOW'    " Operation
        object_name         = ms_item-obj_name    " Object Name
        object_type         = ms_item-obj_type    " Object Type
*        in_new_window       = abap_true
      EXCEPTIONS
        not_executed        = 1
        invalid_object_type = 2
        OTHERS              = 3.

  ENDMETHOD.

  METHOD lif_object~serialize.

    lcx_exception=>raise( 'PRAG not implemented' ).

  ENDMETHOD.

ENDCLASS.

add 'exists' implementation for PRAG

add 'exists' implementation for PRAG


CLASS zcl_abapgit_background DEFINITION PUBLIC CREATE PUBLIC.

  PUBLIC SECTION.
    CLASS-METHODS:
      run
        RAISING zcx_abapgit_exception.

  PRIVATE SECTION.
    CLASS-METHODS:
      build_comment
        IMPORTING is_files          TYPE zif_abapgit_definitions=>ty_stage_files
        RETURNING VALUE(rv_comment) TYPE string,
      push
        IMPORTING io_repo     TYPE REF TO zcl_abapgit_repo_online
                  is_settings TYPE zcl_abapgit_persist_background=>ty_background
        RAISING   zcx_abapgit_exception,
      push_fixed
        IMPORTING io_repo     TYPE REF TO zcl_abapgit_repo_online
                  is_settings TYPE zcl_abapgit_persist_background=>ty_background
        RAISING   zcx_abapgit_exception,
      push_auto
        IMPORTING io_repo TYPE REF TO zcl_abapgit_repo_online
        RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS zcl_abapgit_background IMPLEMENTATION.

  METHOD build_comment.

    DATA: lt_objects TYPE STANDARD TABLE OF string WITH DEFAULT KEY,
          lv_str     TYPE string.

    FIELD-SYMBOLS: <ls_local> LIKE LINE OF is_files-local.

    LOOP AT is_files-local ASSIGNING <ls_local>.
      lv_str = |{ <ls_local>-item-obj_type } { <ls_local>-item-obj_name }|.
      APPEND lv_str TO lt_objects.
    ENDLOOP.

    SORT lt_objects AS TEXT.
    DELETE ADJACENT DUPLICATES FROM lt_objects.

    IF lines( lt_objects ) = 1.
      rv_comment = |BG: { lv_str }|.
    ELSE.
      rv_comment = 'BG: Multiple objects'.
      LOOP AT lt_objects INTO lv_str.
        CONCATENATE rv_comment zif_abapgit_definitions=>gc_newline lv_str INTO rv_comment.
      ENDLOOP.
    ENDIF.

  ENDMETHOD.

  METHOD push.

    IF lines( zcl_abapgit_stage_logic=>get( io_repo )-local ) = 0.
      WRITE: / 'nothing to stage' ##NO_TEXT.
      RETURN.
    ENDIF.

    CASE is_settings-amethod.
      WHEN zcl_abapgit_persist_background=>c_amethod-fixed.
        push_fixed( io_repo     = io_repo
                    is_settings = is_settings ).
      WHEN zcl_abapgit_persist_background=>c_amethod-auto.
        push_auto( io_repo ).
      WHEN OTHERS.
        zcx_abapgit_exception=>raise( 'unknown push method' ).
    ENDCASE.

  ENDMETHOD.

  METHOD push_auto.

    TYPES: BEGIN OF ty_changed,
             filename   TYPE string,
             path       TYPE string,
             changed_by TYPE xubname,
           END OF ty_changed.

    DATA: ls_comment    TYPE zif_abapgit_definitions=>ty_comment,
          ls_files      TYPE zif_abapgit_definitions=>ty_stage_files,
          lt_changed    TYPE STANDARD TABLE OF ty_changed WITH DEFAULT KEY,
          lt_users      TYPE STANDARD TABLE OF xubname WITH DEFAULT KEY,
          ls_user_files LIKE ls_files,
          lv_changed_by TYPE xubname,
          lo_stage      TYPE REF TO zcl_abapgit_stage,
          lt_return     TYPE TABLE OF bapiret2,
          ls_address    TYPE bapiaddr3,
          lt_smtp       TYPE TABLE OF bapiadsmtp,
          ls_smtp       TYPE bapiadsmtp.

    FIELD-SYMBOLS: <ls_changed> LIKE LINE OF lt_changed,
                   <ls_remote>  LIKE LINE OF ls_files-remote,
                   <ls_local>   LIKE LINE OF ls_files-local.

    ls_files = zcl_abapgit_stage_logic=>get( io_repo ).

    LOOP AT ls_files-local ASSIGNING <ls_local>.
      lv_changed_by = zcl_abapgit_objects=>changed_by( <ls_local>-item ).
      APPEND lv_changed_by TO lt_users.
      APPEND INITIAL LINE TO lt_changed ASSIGNING <ls_changed>.
      <ls_changed>-changed_by = lv_changed_by.
      <ls_changed>-filename   = <ls_local>-file-filename.
      <ls_changed>-path       = <ls_local>-file-path.
    ENDLOOP.

    SORT lt_users ASCENDING.
    DELETE ADJACENT DUPLICATES FROM lt_users.

    LOOP AT lt_users INTO lv_changed_by.
      CLEAR: ls_comment, ls_address, lt_return, lt_smtp.

      CALL FUNCTION 'BAPI_USER_GET_DETAIL'
        EXPORTING
          username = lv_changed_by
        IMPORTING
          address  = ls_address
        TABLES
          return   = lt_return
          addsmtp  = lt_smtp.

      LOOP AT lt_smtp INTO ls_smtp.
        ls_comment-committer-email = ls_smtp-e_mail.
      ENDLOOP.

      IF ls_comment-committer-email IS INITIAL.
        ls_comment-committer-email = |{ lv_changed_by }@localhost|.
      ENDIF.

      ls_comment-committer-name = ls_address-fullname.

      IF ls_comment-committer-name IS INITIAL.
        ls_comment-committer-name  = lv_changed_by.
      ENDIF.

      CREATE OBJECT lo_stage
        EXPORTING
          iv_branch_name = io_repo->get_branch_name( )
          iv_branch_sha1 = io_repo->get_sha1_remote( ).

      CLEAR ls_user_files.

      LOOP AT ls_files-local ASSIGNING <ls_local>.
        READ TABLE lt_changed WITH KEY
          path = <ls_local>-file-path
          filename = <ls_local>-file-filename
          changed_by = lv_changed_by
          TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          WRITE: / 'stage' ##NO_TEXT,
            ls_comment-committer-name,
            <ls_local>-file-path,
            <ls_local>-file-filename.

          lo_stage->add( iv_path     = <ls_local>-file-path
                         iv_filename = <ls_local>-file-filename
                         iv_data     = <ls_local>-file-data ).

          APPEND <ls_local> TO ls_user_files-local.

          LOOP AT ls_files-remote ASSIGNING <ls_remote>
              WHERE filename = <ls_local>-file-filename
              AND path <> <ls_local>-file-path
              AND filename <> 'package.devc.xml'.
            WRITE: / 'rm' ##NO_TEXT,
              <ls_remote>-path,
              <ls_remote>-filename.

* rm old file when object has moved
            lo_stage->rm(
              iv_path     = <ls_remote>-path
              iv_filename = <ls_remote>-filename ).
            EXIT. " assumption: only one file
          ENDLOOP.
        ENDIF.
      ENDLOOP.

      ls_comment-comment = build_comment( ls_user_files ).

      io_repo->push( is_comment = ls_comment
                     io_stage   = lo_stage ).
    ENDLOOP.

  ENDMETHOD.

  METHOD push_fixed.

    DATA: ls_comment TYPE zif_abapgit_definitions=>ty_comment,
          ls_files   TYPE zif_abapgit_definitions=>ty_stage_files,
          lo_stage   TYPE REF TO zcl_abapgit_stage.

    FIELD-SYMBOLS: <ls_local> LIKE LINE OF ls_files-local.

    ls_files = zcl_abapgit_stage_logic=>get( io_repo ).
    ASSERT lines( ls_files-local ) > 0.

    CREATE OBJECT lo_stage
      EXPORTING
        iv_branch_name = io_repo->get_branch_name( )
        iv_branch_sha1 = io_repo->get_sha1_remote( ).

    LOOP AT ls_files-local ASSIGNING <ls_local>.
      WRITE: / 'stage' ##NO_TEXT,
        <ls_local>-file-path,
        <ls_local>-file-filename.
      lo_stage->add( iv_path     = <ls_local>-file-path
                     iv_filename = <ls_local>-file-filename
                     iv_data     = <ls_local>-file-data ).
    ENDLOOP.

    ls_comment-committer-name  = is_settings-aname.
    ls_comment-committer-email = is_settings-amail.
    ls_comment-comment         = build_comment( ls_files ).

    io_repo->push( is_comment = ls_comment
                   io_stage   = lo_stage ).

  ENDMETHOD.

  METHOD run.

    CONSTANTS: lc_enq_type TYPE c LENGTH 12 VALUE 'BACKGROUND'.

    DATA: lo_per       TYPE REF TO zcl_abapgit_persist_background,
          lo_repo      TYPE REF TO zcl_abapgit_repo_online,
          lt_list      TYPE zcl_abapgit_persist_background=>tt_background,
          lv_repo_name TYPE string.

    FIELD-SYMBOLS: <ls_list> LIKE LINE OF lt_list.

    CALL FUNCTION 'ENQUEUE_EZABAPGIT'
      EXPORTING
        mode_zabapgit  = 'E'
        type           = lc_enq_type
        _scope         = '3'
      EXCEPTIONS
        foreign_lock   = 1
        system_failure = 2
        OTHERS         = 3.
    IF sy-subrc <> 0.
      WRITE: / 'Another intance of the program is already running'.
      RETURN.
    ENDIF.

    CREATE OBJECT lo_per.
    lt_list = lo_per->list( ).

    WRITE: / 'Background mode' ##NO_TEXT.

    LOOP AT lt_list ASSIGNING <ls_list>.
      lo_repo ?= zcl_abapgit_repo_srv=>get_instance( )->get( <ls_list>-key ).
      lv_repo_name = lo_repo->get_name( ).
      WRITE: / <ls_list>-method, lv_repo_name.

      zcl_abapgit_login_manager=>set(
        iv_uri      = lo_repo->get_url( )
        iv_username = <ls_list>-username
        iv_password = <ls_list>-password ).

      CASE <ls_list>-method.
        WHEN zcl_abapgit_persist_background=>c_method-pull.
          lo_repo->deserialize( ).
        WHEN zcl_abapgit_persist_background=>c_method-push.
          push( io_repo     = lo_repo
                is_settings = <ls_list> ).
        WHEN OTHERS.
          zcx_abapgit_exception=>raise( 'background, unknown mode' ).
      ENDCASE.
    ENDLOOP.

    IF lines( lt_list ) = 0.
      WRITE: / 'Nothing configured' ##NO_TEXT.
    ENDIF.

    CALL FUNCTION 'DEQUEUE_EZABAPGIT'
      EXPORTING
        type = lc_enq_type.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_background DEFINITION PUBLIC CREATE PUBLIC.

  PUBLIC SECTION.
    CLASS-METHODS:
      run
        RAISING zcx_abapgit_exception.

  PRIVATE SECTION.
    CLASS-METHODS:
      build_comment
        IMPORTING is_files          TYPE zif_abapgit_definitions=>ty_stage_files
        RETURNING VALUE(rv_comment) TYPE string,
      push
        IMPORTING io_repo     TYPE REF TO zcl_abapgit_repo_online
                  is_settings TYPE zcl_abapgit_persist_background=>ty_background
        RAISING   zcx_abapgit_exception,
      push_fixed
        IMPORTING io_repo     TYPE REF TO zcl_abapgit_repo_online
                  is_settings TYPE zcl_abapgit_persist_background=>ty_background
        RAISING   zcx_abapgit_exception,
      push_auto
        IMPORTING io_repo TYPE REF TO zcl_abapgit_repo_online
        RAISING   zcx_abapgit_exception,
      determine_user_details
        IMPORTING iv_changed_by  TYPE xubname
        RETURNING VALUE(rs_user) TYPE zif_abapgit_definitions=>ty_git_user.

ENDCLASS.

CLASS zcl_abapgit_background IMPLEMENTATION.

  METHOD build_comment.

    DATA: lt_objects TYPE STANDARD TABLE OF string WITH DEFAULT KEY,
          lv_str     TYPE string.

    FIELD-SYMBOLS: <ls_local> LIKE LINE OF is_files-local.

    LOOP AT is_files-local ASSIGNING <ls_local>.
      lv_str = |{ <ls_local>-item-obj_type } { <ls_local>-item-obj_name }|.
      APPEND lv_str TO lt_objects.
    ENDLOOP.

    SORT lt_objects AS TEXT.
    DELETE ADJACENT DUPLICATES FROM lt_objects.

    IF lines( lt_objects ) = 1.
      rv_comment = |BG: { lv_str }|.
    ELSE.
      rv_comment = 'BG: Multiple objects'.
      LOOP AT lt_objects INTO lv_str.
        CONCATENATE rv_comment zif_abapgit_definitions=>gc_newline lv_str INTO rv_comment.
      ENDLOOP.
    ENDIF.

  ENDMETHOD.

  METHOD push.

    IF lines( zcl_abapgit_stage_logic=>get( io_repo )-local ) = 0.
      WRITE: / 'nothing to stage' ##NO_TEXT.
      RETURN.
    ENDIF.

    CASE is_settings-amethod.
      WHEN zcl_abapgit_persist_background=>c_amethod-fixed.
        push_fixed( io_repo     = io_repo
                    is_settings = is_settings ).
      WHEN zcl_abapgit_persist_background=>c_amethod-auto.
        push_auto( io_repo ).
      WHEN OTHERS.
        zcx_abapgit_exception=>raise( 'unknown push method' ).
    ENDCASE.

  ENDMETHOD.

  METHOD push_auto.

    TYPES: BEGIN OF ty_changed,
             filename   TYPE string,
             path       TYPE string,
             changed_by TYPE xubname,
           END OF ty_changed.

    DATA: ls_comment    TYPE zif_abapgit_definitions=>ty_comment,
          ls_files      TYPE zif_abapgit_definitions=>ty_stage_files,
          lt_changed    TYPE STANDARD TABLE OF ty_changed WITH DEFAULT KEY,
          lt_users      TYPE STANDARD TABLE OF xubname WITH DEFAULT KEY,
          ls_user_files LIKE ls_files,
          lv_changed_by TYPE xubname,
          lo_stage      TYPE REF TO zcl_abapgit_stage.

    FIELD-SYMBOLS: <ls_changed> LIKE LINE OF lt_changed,
                   <ls_remote>  LIKE LINE OF ls_files-remote,
                   <ls_local>   LIKE LINE OF ls_files-local.

    ls_files = zcl_abapgit_stage_logic=>get( io_repo ).

    LOOP AT ls_files-local ASSIGNING <ls_local>.
      lv_changed_by = zcl_abapgit_objects=>changed_by( <ls_local>-item ).
      APPEND lv_changed_by TO lt_users.
      APPEND INITIAL LINE TO lt_changed ASSIGNING <ls_changed>.
      <ls_changed>-changed_by = lv_changed_by.
      <ls_changed>-filename   = <ls_local>-file-filename.
      <ls_changed>-path       = <ls_local>-file-path.
    ENDLOOP.

    SORT lt_users ASCENDING.
    DELETE ADJACENT DUPLICATES FROM lt_users.

    LOOP AT lt_users INTO lv_changed_by.
      CLEAR: ls_comment.

      ls_comment-committer = determine_user_details( lv_changed_by ).

      CREATE OBJECT lo_stage
        EXPORTING
          iv_branch_name = io_repo->get_branch_name( )
          iv_branch_sha1 = io_repo->get_sha1_remote( ).

      CLEAR ls_user_files.

      LOOP AT ls_files-local ASSIGNING <ls_local>.
        READ TABLE lt_changed WITH KEY
          path = <ls_local>-file-path
          filename = <ls_local>-file-filename
          changed_by = lv_changed_by
          TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          WRITE: / 'stage' ##NO_TEXT,
            ls_comment-committer-name,
            <ls_local>-file-path,
            <ls_local>-file-filename.

          lo_stage->add( iv_path     = <ls_local>-file-path
                         iv_filename = <ls_local>-file-filename
                         iv_data     = <ls_local>-file-data ).

          APPEND <ls_local> TO ls_user_files-local.

          LOOP AT ls_files-remote ASSIGNING <ls_remote>
              WHERE filename = <ls_local>-file-filename
              AND path <> <ls_local>-file-path
              AND filename <> 'package.devc.xml'.
            WRITE: / 'rm' ##NO_TEXT,
              <ls_remote>-path,
              <ls_remote>-filename.

* rm old file when object has moved
            lo_stage->rm(
              iv_path     = <ls_remote>-path
              iv_filename = <ls_remote>-filename ).
            EXIT. " assumption: only one file
          ENDLOOP.
        ENDIF.
      ENDLOOP.

      ls_comment-comment = build_comment( ls_user_files ).

      io_repo->push( is_comment = ls_comment
                     io_stage   = lo_stage ).
    ENDLOOP.

  ENDMETHOD.

  METHOD push_fixed.

    DATA: ls_comment TYPE zif_abapgit_definitions=>ty_comment,
          ls_files   TYPE zif_abapgit_definitions=>ty_stage_files,
          lo_stage   TYPE REF TO zcl_abapgit_stage.

    FIELD-SYMBOLS: <ls_local> LIKE LINE OF ls_files-local.

    ls_files = zcl_abapgit_stage_logic=>get( io_repo ).
    ASSERT lines( ls_files-local ) > 0.

    CREATE OBJECT lo_stage
      EXPORTING
        iv_branch_name = io_repo->get_branch_name( )
        iv_branch_sha1 = io_repo->get_sha1_remote( ).

    LOOP AT ls_files-local ASSIGNING <ls_local>.
      WRITE: / 'stage' ##NO_TEXT,
        <ls_local>-file-path,
        <ls_local>-file-filename.
      lo_stage->add( iv_path     = <ls_local>-file-path
                     iv_filename = <ls_local>-file-filename
                     iv_data     = <ls_local>-file-data ).
    ENDLOOP.

    ls_comment-committer-name  = is_settings-aname.
    ls_comment-committer-email = is_settings-amail.
    ls_comment-comment         = build_comment( ls_files ).

    io_repo->push( is_comment = ls_comment
                   io_stage   = lo_stage ).

  ENDMETHOD.

  METHOD run.

    CONSTANTS: lc_enq_type TYPE c LENGTH 12 VALUE 'BACKGROUND'.

    DATA: lo_per       TYPE REF TO zcl_abapgit_persist_background,
          lo_repo      TYPE REF TO zcl_abapgit_repo_online,
          lt_list      TYPE zcl_abapgit_persist_background=>tt_background,
          lv_repo_name TYPE string.

    FIELD-SYMBOLS: <ls_list> LIKE LINE OF lt_list.

    CALL FUNCTION 'ENQUEUE_EZABAPGIT'
      EXPORTING
        mode_zabapgit  = 'E'
        type           = lc_enq_type
        _scope         = '3'
      EXCEPTIONS
        foreign_lock   = 1
        system_failure = 2
        OTHERS         = 3.
    IF sy-subrc <> 0.
      WRITE: / 'Another intance of the program is already running'.
      RETURN.
    ENDIF.

    CREATE OBJECT lo_per.
    lt_list = lo_per->list( ).

    WRITE: / 'Background mode' ##NO_TEXT.

    LOOP AT lt_list ASSIGNING <ls_list>.
      lo_repo ?= zcl_abapgit_repo_srv=>get_instance( )->get( <ls_list>-key ).
      lv_repo_name = lo_repo->get_name( ).
      WRITE: / <ls_list>-method, lv_repo_name.

      zcl_abapgit_login_manager=>set(
        iv_uri      = lo_repo->get_url( )
        iv_username = <ls_list>-username
        iv_password = <ls_list>-password ).

      CASE <ls_list>-method.
        WHEN zcl_abapgit_persist_background=>c_method-pull.
          lo_repo->deserialize( ).
        WHEN zcl_abapgit_persist_background=>c_method-push.
          push( io_repo     = lo_repo
                is_settings = <ls_list> ).
        WHEN OTHERS.
          zcx_abapgit_exception=>raise( 'background, unknown mode' ).
      ENDCASE.
    ENDLOOP.

    IF lines( lt_list ) = 0.
      WRITE: / 'Nothing configured' ##NO_TEXT.
    ENDIF.

    CALL FUNCTION 'DEQUEUE_EZABAPGIT'
      EXPORTING
        type = lc_enq_type.

  ENDMETHOD.

  METHOD determine_user_details.

    DATA: lt_return  TYPE TABLE OF bapiret2,
          ls_address TYPE bapiaddr3,
          lt_smtp    TYPE TABLE OF bapiadsmtp,
          ls_smtp    TYPE bapiadsmtp.

*   Call BAPI to get user details from SU01
    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username = iv_changed_by
      IMPORTING
        address  = ls_address
      TABLES
        return   = lt_return
        addsmtp  = lt_smtp.

*   Choose the first email from SU01
    SORT lt_smtp BY consnumber ASCENDING.

    LOOP AT lt_smtp INTO ls_smtp.
      rs_user-email = ls_smtp-e_mail.
      EXIT.
    ENDLOOP.

*   If no email, fall back to localhost/default email
    IF rs_user-email IS INITIAL.
      rs_user-email = |{ iv_changed_by }@localhost|.
    ENDIF.

*   Attempt to use the full name from SU01
    rs_user-name = ls_address-fullname.

*   If no full name maintained, just use changed by user name
    IF rs_user-name IS INITIAL.
      rs_user-name  = iv_changed_by.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

Refactor ZCL_ABAPGIT_BACKGROUND to new method

Refactor ZCL_ABAPGIT_BACKGROUND with a new method to determine user details in automatic commit background scenarios.


CLASS zcl_wabap_service DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES if_http_extension.

    CONSTANTS cv_url TYPE string VALUE '/SAP/zwabap' ##NO_TEXT.

  PROTECTED SECTION.

    METHODS to_json
      IMPORTING
        !ig_data       TYPE any
      RETURNING
        VALUE(rv_json) TYPE xstring.
    METHODS read_mime
      IMPORTING
        !iv_file TYPE string.

  PRIVATE SECTION.

    DATA mi_server TYPE REF TO if_http_server.
ENDCLASS.

CLASS ZCL_WABAP_SERVICE IMPLEMENTATION.

  METHOD if_http_extension~handle_request.

* see https://github.com/larshp/WABAP/wiki/REST

    DATA: lv_reason TYPE string,
          lv_path   TYPE string,
          lv_name   TYPE tadir-obj_name,
          lt_path   TYPE TABLE OF string.

    mi_server = server.

    lv_path = server->request->get_header_field( '~path_info' ).

    DATA(lv_method) = server->request->get_method( ).

    SPLIT lv_path AT '/' INTO TABLE lt_path.
    READ TABLE lt_path INDEX 5 INTO lv_name.
    TRANSLATE lv_name TO UPPER CASE.

* todo, refactor
    IF lv_path CP '/rest/objects/DEVC/*'.
      DATA(lo_devc) = NEW zcl_wabap_object_devc( lv_name ).
      server->response->set_data( to_json( lo_devc->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/PROG/*/abap'.
      DATA(lo_prog) = NEW zcl_wabap_object_prog( lv_name ).
      server->response->set_header_field(
        name  = 'Content-Type'
        value = 'text/plain' ) ##NO_TEXT.
      server->response->set_cdata( lo_prog->abap( ) ).
    ELSEIF lv_path CP '/rest/objects/PROG/*'.
      lo_prog = NEW zcl_wabap_object_prog( lv_name ).
      server->response->set_data( to_json( lo_prog->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/CLAS/*/abap'.
      DATA(lo_clas) = NEW zcl_wabap_object_clas( lv_name ).
      server->response->set_header_field(
        name  = 'Content-Type'
        value = 'text/plain' ) ##NO_TEXT.
      server->response->set_cdata( lo_clas->abap( ) ).
    ELSEIF lv_path CP '/rest/objects/CLAS/*'.
      lo_clas = NEW zcl_wabap_object_clas( lv_name ).
      server->response->set_data( to_json( lo_clas->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/SMIM/*/content'.
      DATA(lo_smim) = NEW zcl_wabap_object_smim( lv_name ).
      IF lv_method = 'POST'.
        lo_smim->save_content( server->request->get_data( ) ).
      ELSE.
        server->response->set_data( lo_smim->read_content( ) ).
      ENDIF.
    ELSEIF lv_path CP '/rest/objects/SMIM/*'.
      lo_smim = NEW zcl_wabap_object_smim( lv_name ).
      server->response->set_data( to_json( lo_smim->read( ) ) ).

    ELSEIF lv_path CP '/rest/pretty_printer'.
      server->response->set_cdata(
        zcl_wabap_pretty_printer=>run(
        server->request->get_cdata( ) ) ).

    ELSEIF lv_path = '' OR lv_path = '/'.
* todo, redirect "/wabap" to "/wabap/" ?
      read_mime( '/index.html' ).
    ELSE.
      read_mime( lv_path ).
    ENDIF.

    server->response->set_status( code = 200
                                  reason = lv_reason ).

  ENDMETHOD.

  METHOD read_mime.

* todo, does this work with browser caching?
* or use something more standard instead?

    DATA: li_api  TYPE REF TO if_mr_api,
          lv_data TYPE xstring,
          lv_mime TYPE string,
          lv_url  TYPE string.

    lv_url = cv_url && iv_file.

    li_api = cl_mime_repository_api=>if_mr_api~get_api( ).

    li_api->get(
      EXPORTING
        i_url       = lv_url
      IMPORTING
        e_content   = lv_data
        e_mime_type = lv_mime ).

    mi_server->response->set_content_type( lv_mime ).
    mi_server->response->set_data( lv_data ).

  ENDMETHOD.

  METHOD to_json.

    DATA: lo_writer TYPE REF TO cl_sxml_string_writer.

    lo_writer = cl_sxml_string_writer=>create( if_sxml=>co_xt_json ).
    CALL TRANSFORMATION id
      SOURCE data = ig_data
      RESULT XML lo_writer.
    rv_json = lo_writer->get_output( ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_wabap_service DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES if_http_extension.

    CONSTANTS c_url TYPE string VALUE '/SAP/zwabap' ##NO_TEXT.
  PROTECTED SECTION.

    METHODS to_json
      IMPORTING
        !ig_data       TYPE any
      RETURNING
        VALUE(rv_json) TYPE xstring.
    METHODS read_mime
      IMPORTING
        !iv_file TYPE string.

  PRIVATE SECTION.

    DATA mi_server TYPE REF TO if_http_server.
ENDCLASS.

CLASS ZCL_WABAP_SERVICE IMPLEMENTATION.

  METHOD if_http_extension~handle_request.

* see https://github.com/larshp/WABAP/wiki/REST

    DATA: lv_reason TYPE string,
          lv_path   TYPE string,
          lv_name   TYPE tadir-obj_name,
          lt_path   TYPE TABLE OF string.

    mi_server = server.

    lv_path = server->request->get_header_field( '~path_info' ).

    DATA(lv_method) = server->request->get_method( ).

    SPLIT lv_path AT '/' INTO TABLE lt_path.
    READ TABLE lt_path INDEX 5 INTO lv_name.              "#EC CI_SUBRC
    TRANSLATE lv_name TO UPPER CASE.

* todo, refactor
    IF lv_path CP '/rest/objects/DEVC/*'.
      DATA(lo_devc) = NEW zcl_wabap_object_devc( lv_name ).
      server->response->set_data( to_json( lo_devc->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/PROG/*/abap'.
      DATA(lo_prog) = NEW zcl_wabap_object_prog( lv_name ).
      server->response->set_header_field(
        name  = 'Content-Type'
        value = 'text/plain' ) ##NO_TEXT.
      server->response->set_cdata( lo_prog->abap( ) ).
    ELSEIF lv_path CP '/rest/objects/PROG/*'.
      lo_prog = NEW zcl_wabap_object_prog( lv_name ).
      server->response->set_data( to_json( lo_prog->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/CLAS/*/abap'.
      DATA(lo_clas) = NEW zcl_wabap_object_clas( lv_name ).
      server->response->set_header_field(
        name  = 'Content-Type'
        value = 'text/plain' ) ##NO_TEXT.
      server->response->set_cdata( lo_clas->abap( ) ).
    ELSEIF lv_path CP '/rest/objects/CLAS/*'.
      lo_clas = NEW zcl_wabap_object_clas( lv_name ).
      server->response->set_data( to_json( lo_clas->read( ) ) ).

    ELSEIF lv_path CP '/rest/objects/SMIM/*/content'.
      DATA(lo_smim) = NEW zcl_wabap_object_smim( lv_name ).
      IF lv_method = 'POST'.
        lo_smim->save_content( server->request->get_data( ) ).
      ELSE.
        server->response->set_data( lo_smim->read_content( ) ).
      ENDIF.
    ELSEIF lv_path CP '/rest/objects/SMIM/*'.
      lo_smim = NEW zcl_wabap_object_smim( lv_name ).
      server->response->set_data( to_json( lo_smim->read( ) ) ).

    ELSEIF lv_path CP '/rest/pretty_printer'.
      server->response->set_cdata(
        zcl_wabap_pretty_printer=>run(
        server->request->get_cdata( ) ) ).

    ELSEIF lv_path = '' OR lv_path = '/'.
* todo, redirect "/wabap" to "/wabap/" ?
      read_mime( '/index.html' ).
    ELSE.
      read_mime( lv_path ).
    ENDIF.

  ENDMETHOD.

  METHOD read_mime.

    DATA: li_api       TYPE REF TO if_mr_api,
          lv_data      TYPE xstring,
          lv_mime      TYPE string,
          lv_changed   TYPE smimphio-chng_time,
          lv_timestamp TYPE char14,
          lv_modified  TYPE string,
          lv_url       TYPE string.

    lv_url = c_url && iv_file.

    li_api = cl_mime_repository_api=>if_mr_api~get_api( ).

    li_api->get(
      EXPORTING
        i_url                  = lv_url
      IMPORTING
        e_content              = lv_data
        e_mime_type            = lv_mime
        e_content_last_changed = lv_changed
      EXCEPTIONS
        not_found              = 1 ).
    IF sy-subrc = 1.
      mi_server->response->set_cdata( '404' ).
      mi_server->response->set_status( code = 404 reason = '404' ).
      RETURN.
    ENDIF.

    lv_timestamp = lv_changed.
    lv_modified = cl_bsp_utility=>date_to_string_http( lv_timestamp ).
    DATA(lv_value) = mi_server->request->get_header_field(
      name  = 'If-Modified-Since' ) ##NO_TEXT.
    IF lv_modified = lv_value.
      mi_server->response->set_status( code = 304 reason = '' ).
      RETURN.
    ENDIF.

    mi_server->response->set_header_field(
      name  = 'Cache-Control'
      value = 'max-age=86400' ) ##NO_TEXT.

    mi_server->response->set_header_field(
      name  = 'Last-Modified'
      value = lv_modified ) ##NO_TEXT.

    mi_server->response->set_content_type( lv_mime ).
    mi_server->response->set_data( lv_data ).

  ENDMETHOD.

  METHOD to_json.

    DATA: lo_writer TYPE REF TO cl_sxml_string_writer.

    lo_writer = cl_sxml_string_writer=>create( if_sxml=>co_xt_json ).
    CALL TRANSFORMATION id
      SOURCE data = ig_data
      RESULT XML lo_writer.
    rv_json = lo_writer->get_output( ).

  ENDMETHOD.
ENDCLASS.

use browser cache for mimes

use browser cache for mimes


*&---------------------------------------------------------------------*
*&  Include  zabapgit_requirements
*&---------------------------------------------------------------------*

"! Helper class for checking requirements / dependencies
CLASS lcl_requirement_helper DEFINITION FINAL.
  PUBLIC SECTION.
    TYPES:
      BEGIN OF gty_status,
        met               TYPE abap_bool,
        component         TYPE dlvunit,
        description       TYPE text80,
        installed_release TYPE saprelease,
        installed_patch   TYPE sappatchlv,
        required_release  TYPE saprelease,
        required_patch    TYPE sappatchlv,
      END OF gty_status,
      gty_status_tab TYPE STANDARD TABLE OF gty_status WITH DEFAULT KEY.
    CLASS-METHODS:
      "! Check if the given requirements are met with user interaction
      "! <p>
      "! Shows a popup if requested and asks the user if he wants to continue if there are unmet
      "! requirements. If not an exception is raised.
      "! </p>
      "! @parameter it_requirements | The requirements to check
      "! @parameter iv_show_popup | Show popup with requirements
      "! @raising lcx_exception | Cancelled by user or internal error
      check_requirements IMPORTING it_requirements TYPE lcl_dot_abapgit=>ty_requirement_tab
                                   iv_show_popup   TYPE abap_bool DEFAULT abap_true
                         RAISING   lcx_exception,
      "! Get a table with information about each requirement
      "! @parameter it_requirements | Requirements
      "! @parameter rt_status | Result
      "! @raising lcx_exception | Internal error
      get_requirement_met_status IMPORTING it_requirements  TYPE lcl_dot_abapgit=>ty_requirement_tab
                                 RETURNING VALUE(rt_status) TYPE gty_status_tab
                                 RAISING   lcx_exception.
  PROTECTED SECTION.
  PRIVATE SECTION.
    CLASS-METHODS:
      show_requirement_popup IMPORTING it_requirements TYPE gty_status_tab
                             RAISING   lcx_exception,
      version_greater_or_equal IMPORTING is_status      TYPE gty_status
                               RETURNING VALUE(rv_true) TYPE abap_bool.
ENDCLASS.

CLASS lcl_requirement_helper IMPLEMENTATION.
  METHOD check_requirements.
    DATA: lt_met_status TYPE gty_status_tab,
          lv_answer     TYPE c LENGTH 1.

    lt_met_status = get_requirement_met_status( it_requirements ).

    IF iv_show_popup = abap_true.
      show_requirement_popup( lt_met_status ).
    ENDIF.

    LOOP AT lt_met_status TRANSPORTING NO FIELDS WHERE met = abap_false.
      EXIT.
    ENDLOOP.

    IF sy-subrc = 0.
      CALL FUNCTION 'POPUP_TO_CONFIRM'
        EXPORTING
          text_question = 'The project has unmet requirements. Install anyways?'
        IMPORTING
          answer        = lv_answer.
      IF lv_answer <> '1'.
        lcx_exception=>raise( 'Cancelling because of unmet requirements.' ).
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD get_requirement_met_status.
    DATA: lt_installed TYPE STANDARD TABLE OF cvers_sdu.
    FIELD-SYMBOLS: <ls_requirement>    TYPE lcl_dot_abapgit=>ty_requirement,
                   <ls_status>         TYPE gty_status,
                   <ls_installed_comp> TYPE cvers_sdu.

    CALL FUNCTION 'DELIVERY_GET_INSTALLED_COMPS'
      TABLES
        tt_comptab       = lt_installed
      EXCEPTIONS
        no_release_found = 1
        OTHERS           = 2.
    IF sy-subrc <> 0.
      lcx_exception=>raise( |Error from DELIVERY_GET_INSTALLED_COMPS { sy-subrc }| ) ##NO_TEXT.
    ENDIF.

    LOOP AT it_requirements ASSIGNING <ls_requirement>.
      APPEND INITIAL LINE TO rt_status ASSIGNING <ls_status>.
      <ls_status>-component = <ls_requirement>-component.
      <ls_status>-required_release = <ls_requirement>-min_release.
      <ls_status>-required_patch = <ls_requirement>-min_patch.

      READ TABLE lt_installed WITH KEY component = <ls_requirement>-component
                              ASSIGNING <ls_installed_comp>.
      IF sy-subrc = 0.
        <ls_status>-installed_release = <ls_installed_comp>-release.
        <ls_status>-installed_patch = <ls_installed_comp>-extrelease.
        <ls_status>-description = <ls_installed_comp>-desc_text.

        <ls_status>-met = version_greater_or_equal( <ls_status> ).
      ENDIF.

      UNASSIGN <ls_installed_comp>.
    ENDLOOP.
  ENDMETHOD.

  METHOD version_greater_or_equal.
    DATA: lv_number TYPE numc4.

    TRY.
        MOVE EXACT: is_status-installed_release TO lv_number,
                    is_status-installed_patch   TO lv_number,
                    is_status-required_release  TO lv_number,
                    is_status-required_patch    TO lv_number.
      CATCH cx_sy_conversion_error.
        " Cannot compare by number, assume requirement not fullfilled (user can force install
        " anyways if this was an error)
        rv_true = abap_false.
        RETURN.
    ENDTRY.

    " Check if versions are comparable by number
    IF is_status-installed_release > is_status-required_release
       OR ( is_status-installed_release = is_status-required_release
            AND ( is_status-required_patch IS INITIAL OR
                  is_status-installed_patch >= is_status-required_patch ) ).

      rv_true = abap_true.
    ENDIF.
  ENDMETHOD.

  METHOD show_requirement_popup.
    TYPES: BEGIN OF lty_color_line,
             color TYPE lvc_t_scol.
        INCLUDE TYPE gty_status.
    TYPES: END OF lty_color_line,
    lty_color_tab TYPE STANDARD TABLE OF lty_color_line WITH DEFAULT KEY.
    DATA: lo_alv         TYPE REF TO cl_salv_table,
          lo_column      TYPE REF TO cl_salv_column,
          lo_columns     TYPE REF TO cl_salv_columns_table,
          lt_color_table TYPE lty_color_tab,
          lt_color_neg   TYPE lvc_t_scol,
          lt_color_pos   TYPE lvc_t_scol,
          ls_color       TYPE lvc_s_scol,
          lx_ex          TYPE REF TO cx_root.
    FIELD-SYMBOLS: <ls_line> TYPE lty_color_line.

    ls_color-color-col = col_negative.
    APPEND ls_color TO lt_color_neg.

    ls_color-color-col = col_positive.
    APPEND ls_color TO lt_color_pos.

    CLEAR ls_color.

    MOVE-CORRESPONDING it_requirements TO lt_color_table.

    LOOP AT lt_color_table ASSIGNING <ls_line>.
      IF <ls_line>-met = abap_false.
        <ls_line>-color = lt_color_neg.
      ELSE.
        <ls_line>-color = lt_color_pos.
      ENDIF.
    ENDLOOP.
    UNASSIGN <ls_line>.

    TRY.
        cl_salv_table=>factory(
          IMPORTING
            r_salv_table   = lo_alv
          CHANGING
            t_table        = lt_color_table
        ).

        lo_columns = lo_alv->get_columns( ).
        lo_columns->get_column( 'MET' )->set_short_text( 'Met' ).
        lo_columns->set_color_column( 'COLOR' ).
        lo_columns->set_optimize( ).

        lo_column = lo_columns->get_column( 'REQUIRED_RELEASE' ).
        lo_column->set_fixed_header_text( 'S').
        lo_column->set_short_text( 'Req. Rel.' ).

        lo_column = lo_columns->get_column( 'REQUIRED_PATCH' ).
        lo_column->set_fixed_header_text( 'S').
        lo_column->set_short_text( 'Req. SP L.' ).

        lo_alv->set_screen_popup( start_column = 30
                                  end_column   = 100
                                  start_line   = 10
                                  end_line     = 20 ).
        lo_alv->get_display_settings( )->set_list_header( 'Requirements' ).
        lo_alv->display( ).

      CATCH cx_salv_msg cx_salv_not_found cx_salv_data_error INTO lx_ex.
        RAISE EXCEPTION TYPE lcx_exception
          EXPORTING
            iv_text     = lx_ex->get_text( )
            ix_previous = lx_ex.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

Fix code style

Fix code style


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_EXPLORE
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_explore DEFINITION FINAL INHERITING FROM lcl_gui_page.
  PUBLIC SECTION.

    CONSTANTS c_explore_url TYPE string
      VALUE 'http://larshp.github.io/abapGit/explore.html'.

    METHODS constructor.

  PROTECTED SECTION.
    METHODS render_content REDEFINITION.

ENDCLASS.                       "lcl_gui_page_explore DEFINITION

CLASS lcl_gui_page_explore IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-redirect_url = c_explore_url.
  ENDMETHOD.  "constructor

  METHOD render_content.
    ASSERT 1 = 1. " Dummy
  ENDMETHOD. "render_content.

ENDCLASS.                       "lcl_gui_page_explore IMPLEMENTATION

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_EXPLORE
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_explore DEFINITION FINAL INHERITING FROM lcl_gui_page.
  PUBLIC SECTION.

    CONSTANTS c_explore_url TYPE string
      VALUE 'https://dotabap.github.io/explore.html'.

    METHODS constructor.

  PROTECTED SECTION.
    METHODS render_content REDEFINITION.

ENDCLASS.                       "lcl_gui_page_explore DEFINITION

CLASS lcl_gui_page_explore IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-redirect_url = c_explore_url.
  ENDMETHOD.  "constructor

  METHOD render_content.
    ASSERT 1 = 1. " Dummy
  ENDMETHOD. "render_content.

ENDCLASS.                       "lcl_gui_page_explore IMPLEMENTATION

move to new explore page #130

move to new explore page #130


CLASS zcl_ags_repo DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    TYPES:
      ty_repos_tt TYPE STANDARD TABLE OF zags_repos WITH DEFAULT KEY.
    TYPES:
      ty_branches_tt TYPE STANDARD TABLE OF REF TO zcl_ags_branch WITH DEFAULT KEY.

    CLASS-METHODS create
      IMPORTING
        !iv_name        TYPE zags_repos-name
        !iv_description TYPE zags_repos-description
      RETURNING
        VALUE(ro_repo)  TYPE REF TO zcl_ags_repo
      RAISING
        zcx_ags_error.
    CLASS-METHODS list
      RETURNING
        VALUE(rt_list) TYPE ty_repos_tt.
    METHODS constructor
      IMPORTING
        !iv_name TYPE zags_repos-name
      RAISING
        zcx_ags_error.
    METHODS delete
      RAISING
        zcx_ags_error.
    METHODS get_branch
      IMPORTING
        !iv_branch_name  TYPE zags_branch_name
      RETURNING
        VALUE(ro_branch) TYPE REF TO zcl_ags_branch
      RAISING
        zcx_ags_error.
    METHODS get_data
      RETURNING
        VALUE(rs_data) TYPE zags_repos.
    METHODS list_branches
      RETURNING
        VALUE(rt_list) TYPE ty_branches_tt
      RAISING
        zcx_ags_error.
    METHODS set_description
      IMPORTING
        !iv_description TYPE zags_repos-description
      RAISING
        zcx_ags_error.
  PROTECTED SECTION.
  PRIVATE SECTION.

    DATA ms_data TYPE zags_repos.

    CLASS-METHODS initial_commit
      RETURNING
        VALUE(rv_commit) TYPE zags_sha1
      RAISING
        zcx_ags_error.
ENDCLASS.

CLASS ZCL_AGS_REPO IMPLEMENTATION.

  METHOD constructor.

    SELECT SINGLE * FROM zags_repos
      INTO ms_data
      WHERE name = iv_name.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m002.
    ENDIF.

  ENDMETHOD.

  METHOD create.

    DATA: lt_list TYPE ty_repos_tt,
          ls_repo TYPE zags_repos.

    ASSERT NOT iv_name CA '/\'.
    ASSERT NOT iv_name IS INITIAL.

    lt_list = list( ).
    READ TABLE lt_list WITH KEY name = iv_name TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m001.
    ENDIF.

    ls_repo-repo = zcl_ags_util=>uuid( ).
    ls_repo-name = iv_name.
    ls_repo-description = iv_description.
    ls_repo-head = 'master' ##no_text.

    INSERT zags_repos FROM ls_repo.
    ASSERT sy-subrc = 0.

    CREATE OBJECT ro_repo
      EXPORTING
        iv_name = iv_name.

    zcl_ags_branch=>create(
      io_repo   = ro_repo
      iv_name   = ls_repo-head
      iv_commit = initial_commit( ) ).

  ENDMETHOD.

  METHOD delete.

    DATA: lt_branches TYPE ty_branches_tt.

    FIELD-SYMBOLS: <lo_branch> LIKE LINE OF lt_branches.

    lt_branches = list_branches( ).
    LOOP AT lt_branches ASSIGNING <lo_branch>.
      <lo_branch>->delete( ).
    ENDLOOP.

    DELETE FROM zags_repos WHERE name = ms_data-name.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD get_branch.

    DATA: lt_branches TYPE ty_branches_tt.

    lt_branches = list_branches( ).
    LOOP AT lt_branches INTO ro_branch.
      IF ro_branch->get_data( )-name = iv_branch_name.
        RETURN.
      ENDIF.
    ENDLOOP.

    ASSERT 0 = 1.

  ENDMETHOD.

  METHOD get_data.

    rs_data = ms_data.

  ENDMETHOD.

  METHOD initial_commit.

    DATA: lo_blob   TYPE REF TO zcl_ags_obj_blob,
          lo_commit TYPE REF TO zcl_ags_obj_commit,
          lo_tree   TYPE REF TO zcl_ags_obj_tree,
          lv_user   TYPE string.

    CREATE OBJECT lo_blob.
    lo_blob->set_data( zcl_ags_util=>string_to_xstring_utf8( 'test' ) ) ##no_text.
    lo_blob->save( ).

    CREATE OBJECT lo_tree.
    lo_tree->add_file( iv_chmod = zcl_ags_obj_tree=>c_chmod-file
                       iv_name  = 'test.txt'
                       iv_sha1  = lo_blob->sha1( ) ) ##no_text.
    lo_tree->save( ).

    lv_user = |initial <foo@bar.com> { zcl_ags_util=>get_time( ) }|.

    CREATE OBJECT lo_commit.
    lo_commit->set_tree( lo_tree->sha1( ) ).
    lo_commit->set_author( lv_user ) ##no_text.
    lo_commit->set_committer( lv_user ) ##no_text.
    lo_commit->set_body( 'initial' ) ##no_text.
    lo_commit->save( ).

    rv_commit = lo_commit->sha1( ).

  ENDMETHOD.

  METHOD list.

    SELECT * FROM zags_repos
      INTO TABLE rt_list
      ORDER BY name ASCENDING.                          "#EC CI_NOWHERE

  ENDMETHOD.

  METHOD list_branches.

    DATA: lt_list   TYPE TABLE OF zags_branches-name,
          lo_branch TYPE REF TO zcl_ags_branch.

    FIELD-SYMBOLS: <lv_list> LIKE LINE OF lt_list.

    SELECT name FROM zags_branches
      INTO TABLE lt_list
      WHERE repo = ms_data-repo
      ORDER BY name ASCENDING.

    LOOP AT lt_list ASSIGNING <lv_list>.
      CREATE OBJECT lo_branch
        EXPORTING
          io_repo = me
          iv_name = <lv_list>.

      APPEND lo_branch TO rt_list.
    ENDLOOP.

  ENDMETHOD.

  METHOD set_description.

    UPDATE zags_repos SET
      description = iv_description
      WHERE name = ms_data-name.
    ASSERT sy-subrc = 0.

    ms_data-description = iv_description.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_ags_repo DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    TYPES:
      ty_repos_tt TYPE STANDARD TABLE OF zags_repos WITH DEFAULT KEY.
    TYPES:
      ty_branches_tt TYPE STANDARD TABLE OF REF TO zcl_ags_branch WITH DEFAULT KEY.

    CLASS-METHODS create
      IMPORTING
        !iv_name        TYPE zags_repos-name
        !iv_description TYPE zags_repos-description
      RETURNING
        VALUE(ro_repo)  TYPE REF TO zcl_ags_repo
      RAISING
        zcx_ags_error.
    CLASS-METHODS list
      RETURNING
        VALUE(rt_list) TYPE ty_repos_tt.
    METHODS constructor
      IMPORTING
        !iv_name TYPE zags_repos-name
      RAISING
        zcx_ags_error.
    METHODS delete
      RAISING
        zcx_ags_error.
    METHODS get_branch
      IMPORTING
        !iv_branch_name  TYPE zags_branch_name
      RETURNING
        VALUE(ro_branch) TYPE REF TO zcl_ags_branch
      RAISING
        zcx_ags_error.
    METHODS get_data
      RETURNING
        VALUE(rs_data) TYPE zags_repos.
    METHODS list_branches
      RETURNING
        VALUE(rt_list) TYPE ty_branches_tt
      RAISING
        zcx_ags_error.
    METHODS set_description
      IMPORTING
        !iv_description TYPE zags_repos-description
      RAISING
        zcx_ags_error.
  PROTECTED SECTION.
  PRIVATE SECTION.

    DATA ms_data TYPE zags_repos.

    CLASS-METHODS initial_commit
      IMPORTING
        !iv_name         TYPE clike
      RETURNING
        VALUE(rv_commit) TYPE zags_sha1
      RAISING
        zcx_ags_error.
ENDCLASS.

CLASS ZCL_AGS_REPO IMPLEMENTATION.

  METHOD constructor.

    SELECT SINGLE * FROM zags_repos
      INTO ms_data
      WHERE name = iv_name.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m002.
    ENDIF.

  ENDMETHOD.

  METHOD create.

    DATA: lt_list TYPE ty_repos_tt,
          ls_repo TYPE zags_repos.

    ASSERT NOT iv_name CA '/\'.
    ASSERT NOT iv_name IS INITIAL.

    lt_list = list( ).
    READ TABLE lt_list WITH KEY name = iv_name TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m001.
    ENDIF.

    ls_repo-repo = zcl_ags_util=>uuid( ).
    ls_repo-name = iv_name.
    ls_repo-description = iv_description.
    ls_repo-head = 'master' ##no_text.

    INSERT zags_repos FROM ls_repo.
    ASSERT sy-subrc = 0.

    CREATE OBJECT ro_repo
      EXPORTING
        iv_name = iv_name.

    zcl_ags_branch=>create(
      io_repo   = ro_repo
      iv_name   = ls_repo-head
      iv_commit = initial_commit( iv_name ) ).

  ENDMETHOD.

  METHOD delete.

    DATA: lt_branches TYPE ty_branches_tt.

    FIELD-SYMBOLS: <lo_branch> LIKE LINE OF lt_branches.

    lt_branches = list_branches( ).
    LOOP AT lt_branches ASSIGNING <lo_branch>.
      <lo_branch>->delete( ).
    ENDLOOP.

    DELETE FROM zags_repos WHERE name = ms_data-name.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD get_branch.

    DATA: lt_branches TYPE ty_branches_tt.

    lt_branches = list_branches( ).
    LOOP AT lt_branches INTO ro_branch.
      IF ro_branch->get_data( )-name = iv_branch_name.
        RETURN.
      ENDIF.
    ENDLOOP.

    ASSERT 0 = 1.

  ENDMETHOD.

  METHOD get_data.

    rs_data = ms_data.

  ENDMETHOD.

  METHOD initial_commit.

    DATA: lo_blob   TYPE REF TO zcl_ags_obj_blob,
          lo_commit TYPE REF TO zcl_ags_obj_commit,
          lo_tree   TYPE REF TO zcl_ags_obj_tree,
          lv_str    TYPE string,
          lv_user   TYPE string.

    CREATE OBJECT lo_blob.
    lv_str = iv_name.
    lo_blob->set_data( zcl_ags_util=>string_to_xstring_utf8( lv_str ) ).
    lo_blob->save( ).

    CREATE OBJECT lo_tree.
    lo_tree->add_file( iv_chmod = zcl_ags_obj_tree=>c_chmod-file
                       iv_name  = 'README.md'
                       iv_sha1  = lo_blob->sha1( ) ) ##no_text.
    lo_tree->save( ).

    lv_user = |initial <foo@bar.com> { zcl_ags_util=>get_time( ) }|.

    CREATE OBJECT lo_commit.
    lo_commit->set_tree( lo_tree->sha1( ) ).
    lo_commit->set_author( lv_user ).
    lo_commit->set_committer( lv_user ).
    lo_commit->set_body( 'initial' ) ##no_text.
    lo_commit->save( ).

    rv_commit = lo_commit->sha1( ).

  ENDMETHOD.

  METHOD list.

    SELECT * FROM zags_repos
      INTO TABLE rt_list
      ORDER BY name ASCENDING.                          "#EC CI_NOWHERE

  ENDMETHOD.

  METHOD list_branches.

    DATA: lt_list   TYPE TABLE OF zags_branches-name,
          lo_branch TYPE REF TO zcl_ags_branch.

    FIELD-SYMBOLS: <lv_list> LIKE LINE OF lt_list.

    SELECT name FROM zags_branches
      INTO TABLE lt_list
      WHERE repo = ms_data-repo
      ORDER BY name ASCENDING.

    LOOP AT lt_list ASSIGNING <lv_list>.
      CREATE OBJECT lo_branch
        EXPORTING
          io_repo = me
          iv_name = <lv_list>.

      APPEND lo_branch TO rt_list.
    ENDLOOP.

  ENDMETHOD.

  METHOD set_description.

    UPDATE zags_repos SET
      description = iv_description
      WHERE name = ms_data-name.
    ASSERT sy-subrc = 0.

    ms_data-description = iv_description.

  ENDMETHOD.
ENDCLASS.

add README.md instead of test.txt, fix #38


class ZCL_AGS_DB_TREE_CACHE definition
  public
  create private

  global friends ZCL_AGS_DB .

public section.

  methods DELETE_ALL .
  methods INSERT
    importing
      !IT_DATA type ZAGS_TREE_CACHE_TT .
  methods SELECT
    importing
      !IV_REPO type ZAGS_TREE_CACHE-REPO
      !IV_COMMIT_SHA1 type ZAGS_TREE_CACHE-COMMIT_SHA1
      !IV_MAX type I default 0
    returning
      value(RT_DATA) type ZAGS_TREE_CACHE_TT
    raising
      ZCX_AGS_ERROR .
  PROTECTED SECTION.
  PRIVATE SECTION.

    DATA mt_cache TYPE zags_tree_cache_tt .
    DATA mv_fake TYPE abap_bool .

    METHODS set_fake .
ENDCLASS.

CLASS ZCL_AGS_DB_TREE_CACHE IMPLEMENTATION.

  METHOD delete_all.

    IF mv_fake = abap_true.
      CLEAR mt_cache.
    ELSE.
      DELETE FROM zags_tree_cache.                        "#EC CI_SUBRC
    ENDIF.

  ENDMETHOD.

  METHOD insert.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF it_data.

    IF mv_fake = abap_true.
      LOOP AT it_data ASSIGNING <ls_data>.
        INSERT <ls_data> INTO TABLE mt_cache.
        ASSERT sy-subrc = 0.
      ENDLOOP.
    ELSE.
      INSERT zags_tree_cache FROM TABLE it_data.          "#EC CI_SUBRC
    ENDIF.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD select.

    FIELD-SYMBOLS: <ls_cache> LIKE LINE OF mt_cache.

    ASSERT NOT iv_repo IS INITIAL.
    ASSERT NOT iv_commit_sha1 IS INITIAL.

    IF mv_fake = abap_true.
      LOOP AT mt_cache ASSIGNING <ls_cache>
          WHERE repo = iv_repo AND commit_sha1 = iv_commit_sha1.
        APPEND <ls_cache> TO rt_data.
        IF iv_max <> 0 AND lines( rt_data ) = iv_max.
          RETURN.
        ENDIF.
      ENDLOOP.
    ELSE.
      SELECT * FROM zags_tree_cache
        INTO TABLE rt_data
        UP TO iv_max ROWS
        WHERE repo = iv_repo
        AND commit_sha1 = iv_commit_sha1.                 "#EC CI_SUBRC
    ENDIF.

  ENDMETHOD.

  METHOD set_fake.

    mv_fake = abap_true.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_ags_db_tree_cache DEFINITION
  PUBLIC
  CREATE PRIVATE

  GLOBAL FRIENDS zcl_ags_db .

  PUBLIC SECTION.

    METHODS delete_all .
    METHODS insert
      IMPORTING
        !it_data TYPE zags_tree_cache_tt .
    METHODS select
      IMPORTING
        !iv_repo        TYPE zags_tree_cache-repo
        !iv_commit_sha1 TYPE zags_tree_cache-commit_sha1
        !iv_max         TYPE i DEFAULT 0
      RETURNING
        VALUE(rt_data)  TYPE zags_tree_cache_tt
      RAISING
        zcx_ags_error .
  PROTECTED SECTION.
  PRIVATE SECTION.

    DATA mt_cache TYPE zags_tree_cache_tt .
    DATA mv_fake TYPE abap_bool .

    METHODS set_fake .
ENDCLASS.

CLASS ZCL_AGS_DB_TREE_CACHE IMPLEMENTATION.

  METHOD delete_all.

    IF mv_fake = abap_true.
      CLEAR mt_cache.
    ELSE.
      DELETE FROM zags_tree_cache.                        "#EC CI_SUBRC
    ENDIF.

  ENDMETHOD.

  METHOD insert.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF it_data.

    IF mv_fake = abap_true.
      LOOP AT it_data ASSIGNING <ls_data>.
        INSERT <ls_data> INTO TABLE mt_cache.
        ASSERT sy-subrc = 0.
      ENDLOOP.
    ELSE.
      INSERT zags_tree_cache FROM TABLE it_data.          "#EC CI_SUBRC
    ENDIF.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD select.

    FIELD-SYMBOLS: <ls_cache> LIKE LINE OF mt_cache.

    ASSERT NOT iv_repo IS INITIAL.
    ASSERT NOT iv_commit_sha1 IS INITIAL.

    IF mv_fake = abap_true.
      LOOP AT mt_cache ASSIGNING <ls_cache>
          WHERE repo = iv_repo AND commit_sha1 = iv_commit_sha1.
        APPEND <ls_cache> TO rt_data.
        IF iv_max <> 0 AND lines( rt_data ) = iv_max.
          RETURN.
        ENDIF.
      ENDLOOP.
    ELSE.
      SELECT * FROM zags_tree_cache
        INTO TABLE rt_data
        UP TO iv_max ROWS
        WHERE repo = iv_repo
        AND commit_sha1 = iv_commit_sha1.                 "#EC CI_SUBRC
    ENDIF.

  ENDMETHOD.

  METHOD set_fake.

    mv_fake = abap_true.

  ENDMETHOD.
ENDCLASS.

fix indentation

fix indentation


CLASS zcl_abapgit_repo_srv DEFINITION
  PUBLIC
  FINAL
  CREATE PRIVATE .

  PUBLIC SECTION.

    INTERFACES zif_abapgit_repo_srv .

    CLASS-METHODS get_instance
      RETURNING
        VALUE(ri_srv) TYPE REF TO zif_abapgit_repo_srv .
  PRIVATE SECTION.

    ALIASES delete
      FOR zif_abapgit_repo_srv~delete .
    ALIASES get
      FOR zif_abapgit_repo_srv~get .
    ALIASES list
      FOR zif_abapgit_repo_srv~list .
    ALIASES validate_package
      FOR zif_abapgit_repo_srv~validate_package .

    CLASS-DATA gi_ref TYPE REF TO zif_abapgit_repo_srv .
    DATA mv_init TYPE abap_bool VALUE abap_false ##NO_TEXT.
    DATA mo_persistence TYPE REF TO zcl_abapgit_persistence_repo .
    DATA mt_list TYPE zif_abapgit_definitions=>ty_repo_ref_tt .

    METHODS refresh
      RAISING
        zcx_abapgit_exception .
    METHODS constructor .
    METHODS is_sap_object_allowed
      RETURNING
        VALUE(rv_allowed) TYPE abap_bool .
    METHODS add
      IMPORTING
        io_repo TYPE REF TO zcl_abapgit_repo
      RAISING
        zcx_abapgit_exception .
    METHODS validate_sub_super_packages
      IMPORTING
        iv_package TYPE devclass
        it_repos   TYPE zif_abapgit_persistence=>tt_repo
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_REPO_SRV IMPLEMENTATION.

  METHOD add.

    DATA: lo_repo LIKE LINE OF mt_list.

    LOOP AT mt_list INTO lo_repo.
      IF lo_repo->get_key( ) = io_repo->get_key( ).
        IF lo_repo = io_repo.
          RETURN.
        ENDIF.
        zcx_abapgit_exception=>raise( 'identical keys' ).
      ENDIF.
    ENDLOOP.

    APPEND io_repo TO mt_list.

  ENDMETHOD.                    "add

  METHOD constructor.
    CREATE OBJECT mo_persistence.
  ENDMETHOD.                    "class_constructor

  METHOD get_instance.
    IF gi_ref IS INITIAL.
      CREATE OBJECT gi_ref TYPE zcl_abapgit_repo_srv.
    ENDIF.
    ri_srv = gi_ref.
  ENDMETHOD.

  METHOD is_sap_object_allowed.

    rv_allowed = cl_enh_badi_def_utility=>is_sap_system( ).
    IF rv_allowed = abap_true.
      RETURN.
    ENDIF.

    rv_allowed = zcl_abapgit_exit=>get_instance( )->allow_sap_objects( ).

  ENDMETHOD.

  METHOD refresh.

    DATA: lt_list    TYPE zif_abapgit_persistence=>tt_repo,
          lo_online  TYPE REF TO zcl_abapgit_repo_online,
          lo_offline TYPE REF TO zcl_abapgit_repo_offline.

    FIELD-SYMBOLS: <ls_list> LIKE LINE OF lt_list.

    CLEAR mt_list.

    lt_list = mo_persistence->list( ).
    LOOP AT lt_list ASSIGNING <ls_list>.
      IF <ls_list>-offline = abap_false.
        CREATE OBJECT lo_online
          EXPORTING
            is_data = <ls_list>.
        APPEND lo_online TO mt_list.
      ELSE.
        CREATE OBJECT lo_offline
          EXPORTING
            is_data = <ls_list>.
        APPEND lo_offline TO mt_list.
      ENDIF.
    ENDLOOP.

    mv_init = abap_true.

  ENDMETHOD.                    "refresh

  METHOD validate_sub_super_packages.
    DATA:
      ls_repo     LIKE LINE OF it_repos,
      lo_package  TYPE REF TO zif_abapgit_sap_package,
      lt_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt,
      lo_repo     TYPE REF TO zcl_abapgit_repo.

    LOOP AT it_repos INTO ls_repo.
      lo_repo = get( ls_repo-key ).

      lo_package = zcl_abapgit_factory=>get_sap_package( ls_repo-package ).
      APPEND LINES OF lo_package->list_subpackages( ) TO lt_packages.
      APPEND LINES OF lo_package->list_superpackages( ) TO lt_packages.
      READ TABLE lt_packages TRANSPORTING NO FIELDS
        WITH KEY table_line = iv_package.
      IF sy-subrc = 0.
        zcx_abapgit_exception=>raise( |Repository { lo_repo->get_name( ) } already contains { iv_package } | ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~delete.

    io_repo->delete( ).

    DELETE TABLE mt_list FROM io_repo.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~get.

    FIELD-SYMBOLS: <lo_list> LIKE LINE OF mt_list.

    IF mv_init = abap_false.
      refresh( ).
    ENDIF.

    LOOP AT mt_list ASSIGNING <lo_list>.
      IF <lo_list>->get_key( ) = iv_key.
        ro_repo = <lo_list>.
        RETURN.
      ENDIF.
    ENDLOOP.

    zcx_abapgit_exception=>raise( 'repo not found, get' ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~is_repo_installed.

    DATA: lt_repo        TYPE zif_abapgit_definitions=>ty_repo_ref_tt,
          lo_repo        TYPE REF TO zcl_abapgit_repo,
          lv_url         TYPE string,
          lv_package     TYPE devclass,
          lo_repo_online TYPE REF TO zcl_abapgit_repo_online,
          lv_err         TYPE string.

    lt_repo = list( ).

    LOOP AT lt_repo INTO lo_repo.
      CHECK lo_repo->is_offline( ) = abap_false.
      lo_repo_online ?= lo_repo.

      lv_url     = lo_repo_online->get_url( ).
      lv_package = lo_repo_online->get_package( ).
      CHECK to_upper( lv_url ) = to_upper( iv_url ).

      " Validate bindings
      "TODO refactor: move this message out of this method
      IF iv_target_package IS NOT INITIAL AND iv_target_package <> lv_package.
        lv_err = |Installation to package { lv_package } detected. |
              && |Cancelling installation|.
        zcx_abapgit_exception=>raise( lv_err ).
      ENDIF.

      rv_installed = abap_true.
      EXIT.
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~list.

    IF mv_init = abap_false.
      refresh( ).
    ENDIF.

    rt_list = mt_list.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~new_offline.

    DATA: ls_repo TYPE zif_abapgit_persistence=>ty_repo,
          lv_key  TYPE zif_abapgit_persistence=>ty_repo-key.

    validate_package( iv_package ).

    lv_key = mo_persistence->add(
      iv_url         = iv_url
      iv_branch_name = ''
      iv_package     = iv_package
      iv_offline     = abap_true
      is_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( )->get_data( ) ).

    TRY.
        ls_repo = mo_persistence->read( lv_key ).
      CATCH zcx_abapgit_not_found.
        zcx_abapgit_exception=>raise( 'new_offline not found' ).
    ENDTRY.

    CREATE OBJECT ro_repo
      EXPORTING
        is_data = ls_repo.

    add( ro_repo ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~new_online.

    DATA: ls_repo TYPE zif_abapgit_persistence=>ty_repo,
          lv_key  TYPE zif_abapgit_persistence=>ty_repo-key.

    ASSERT NOT iv_url IS INITIAL
      AND NOT iv_branch_name IS INITIAL
      AND NOT iv_package IS INITIAL.

    validate_package( iv_package ).
    zcl_abapgit_url=>validate( |{ iv_url }| ).

    lv_key = mo_persistence->add(
      iv_url         = iv_url
      iv_branch_name = iv_branch_name
      iv_package     = iv_package
      iv_offline     = abap_false
      is_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( )->get_data( ) ).
    TRY.
        ls_repo = mo_persistence->read( lv_key ).
      CATCH zcx_abapgit_not_found.
        zcx_abapgit_exception=>raise( 'new_online not found' ).
    ENDTRY.

    CREATE OBJECT ro_repo
      EXPORTING
        is_data = ls_repo.

    add( ro_repo ).

    ro_repo->refresh( ).
    ro_repo->find_remote_dot_abapgit( ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~purge.

* todo, this should be a method on the repo instead

    DATA: lt_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt.

    IF io_repo->get_local_settings( )-write_protected = abap_true.
      zcx_abapgit_exception=>raise( 'Cannot purge. Local code is write-protected by repo config' ).
    ELSEIF zcl_abapgit_auth=>is_allowed( zif_abapgit_auth=>gc_authorization-uninstall ) = abap_false.
      zcx_abapgit_exception=>raise( 'Not authorized' ).
    ENDIF.

    lt_tadir = zcl_abapgit_factory=>get_tadir( )->read( io_repo->get_package( ) ).

    zcl_abapgit_objects=>delete( it_tadir  = lt_tadir
                                 is_checks = is_checks ).

    delete( io_repo ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~switch_repo_type.

* todo, this should be a method on the repo instead?

    DATA lo_repo TYPE REF TO zcl_abapgit_repo.

    FIELD-SYMBOLS <lo_repo> LIKE LINE OF mt_list.

    lo_repo = get( iv_key ).
    READ TABLE mt_list ASSIGNING <lo_repo> FROM lo_repo.
    ASSERT sy-subrc IS INITIAL.
    ASSERT iv_offline <> lo_repo->ms_data-offline.

    IF iv_offline = abap_true. " On-line -> OFFline
      lo_repo->set(
        iv_url         = zcl_abapgit_url=>name( lo_repo->ms_data-url )
        iv_branch_name = ''
        iv_head_branch = ''
        iv_offline     = abap_true ).
      CREATE OBJECT <lo_repo> TYPE zcl_abapgit_repo_offline
        EXPORTING
          is_data = lo_repo->ms_data.
    ELSE. " OFFline -> On-line
      lo_repo->set( iv_offline = abap_false ).
      CREATE OBJECT <lo_repo> TYPE zcl_abapgit_repo_online
        EXPORTING
          is_data = lo_repo->ms_data.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~validate_package.

    DATA: lv_as4user TYPE tdevc-as4user,
          lt_repos   TYPE zif_abapgit_persistence=>tt_repo.

    IF iv_package IS INITIAL.
      zcx_abapgit_exception=>raise( 'add, package empty' ).
    ENDIF.

    IF iv_package = '$TMP'.
      zcx_abapgit_exception=>raise( 'not possible to use $TMP, create new (local) package' ).
    ENDIF.

    SELECT SINGLE as4user FROM tdevc
      INTO lv_as4user
      WHERE devclass = iv_package.                      "#EC CI_GENBUFF
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Package { iv_package } not found| ).
    ENDIF.

    IF is_sap_object_allowed( ) = abap_false AND lv_as4user = 'SAP'.
      zcx_abapgit_exception=>raise( |Package { iv_package } not allowed| ).
    ENDIF.

    " make sure its not already in use for a different repository
    lt_repos = mo_persistence->list( ).
    READ TABLE lt_repos WITH KEY package = iv_package TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Package { iv_package } already in use| ).
    ENDIF.

    validate_sub_super_packages(
      iv_package = iv_package
      it_repos   = lt_repos ).
  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_repo_srv DEFINITION
  PUBLIC
  FINAL
  CREATE PRIVATE .

  PUBLIC SECTION.

    INTERFACES zif_abapgit_repo_srv .

    CLASS-METHODS get_instance
      RETURNING
        VALUE(ri_srv) TYPE REF TO zif_abapgit_repo_srv .
  PRIVATE SECTION.

    ALIASES delete
      FOR zif_abapgit_repo_srv~delete .
    ALIASES get
      FOR zif_abapgit_repo_srv~get .
    ALIASES list
      FOR zif_abapgit_repo_srv~list .
    ALIASES validate_package
      FOR zif_abapgit_repo_srv~validate_package .

    CLASS-DATA gi_ref TYPE REF TO zif_abapgit_repo_srv .
    DATA mv_init TYPE abap_bool VALUE abap_false ##NO_TEXT.
    DATA mo_persistence TYPE REF TO zcl_abapgit_persistence_repo .
    DATA mt_list TYPE zif_abapgit_definitions=>ty_repo_ref_tt .

    METHODS refresh
      RAISING
        zcx_abapgit_exception .
    METHODS constructor .
    METHODS is_sap_object_allowed
      RETURNING
        VALUE(rv_allowed) TYPE abap_bool .
    METHODS add
      IMPORTING
        io_repo TYPE REF TO zcl_abapgit_repo
      RAISING
        zcx_abapgit_exception .
    METHODS validate_sub_super_packages
      IMPORTING
        iv_package TYPE devclass
        it_repos   TYPE zif_abapgit_persistence=>tt_repo
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_REPO_SRV IMPLEMENTATION.

  METHOD add.

    DATA: lo_repo LIKE LINE OF mt_list.

    LOOP AT mt_list INTO lo_repo.
      IF lo_repo->get_key( ) = io_repo->get_key( ).
        IF lo_repo = io_repo.
          RETURN.
        ENDIF.
        zcx_abapgit_exception=>raise( 'identical keys' ).
      ENDIF.
    ENDLOOP.

    APPEND io_repo TO mt_list.

  ENDMETHOD.                    "add

  METHOD constructor.
    CREATE OBJECT mo_persistence.
  ENDMETHOD.                    "class_constructor

  METHOD get_instance.
    IF gi_ref IS INITIAL.
      CREATE OBJECT gi_ref TYPE zcl_abapgit_repo_srv.
    ENDIF.
    ri_srv = gi_ref.
  ENDMETHOD.

  METHOD is_sap_object_allowed.

    rv_allowed = cl_enh_badi_def_utility=>is_sap_system( ).
    IF rv_allowed = abap_true.
      RETURN.
    ENDIF.

    rv_allowed = zcl_abapgit_exit=>get_instance( )->allow_sap_objects( ).

  ENDMETHOD.

  METHOD refresh.

    DATA: lt_list    TYPE zif_abapgit_persistence=>tt_repo,
          lo_online  TYPE REF TO zcl_abapgit_repo_online,
          lo_offline TYPE REF TO zcl_abapgit_repo_offline.

    FIELD-SYMBOLS: <ls_list> LIKE LINE OF lt_list.

    CLEAR mt_list.

    lt_list = mo_persistence->list( ).
    LOOP AT lt_list ASSIGNING <ls_list>.
      IF <ls_list>-offline = abap_false.
        CREATE OBJECT lo_online
          EXPORTING
            is_data = <ls_list>.
        APPEND lo_online TO mt_list.
      ELSE.
        CREATE OBJECT lo_offline
          EXPORTING
            is_data = <ls_list>.
        APPEND lo_offline TO mt_list.
      ENDIF.
    ENDLOOP.

    mv_init = abap_true.

  ENDMETHOD.                    "refresh

  METHOD validate_sub_super_packages.
    DATA:
      ls_repo     LIKE LINE OF it_repos,
      lo_package  TYPE REF TO zif_abapgit_sap_package,
      lt_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt,
      lo_repo     TYPE REF TO zcl_abapgit_repo.

    LOOP AT it_repos INTO ls_repo.
      lo_repo = get( ls_repo-key ).

      lo_package = zcl_abapgit_factory=>get_sap_package( ls_repo-package ).

      CLEAR lt_packages.
      IF lo_repo->get_local_settings( )-ignore_subpackages = abap_false.
        APPEND LINES OF lo_package->list_subpackages( ) TO lt_packages.
      ENDIF.
      APPEND LINES OF lo_package->list_superpackages( ) TO lt_packages.

      READ TABLE lt_packages TRANSPORTING NO FIELDS
        WITH KEY table_line = iv_package.
      IF sy-subrc = 0.
        zcx_abapgit_exception=>raise( |Repository { lo_repo->get_name( ) } already contains { iv_package } | ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~delete.

    io_repo->delete( ).

    DELETE TABLE mt_list FROM io_repo.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~get.

    FIELD-SYMBOLS: <lo_list> LIKE LINE OF mt_list.

    IF mv_init = abap_false.
      refresh( ).
    ENDIF.

    LOOP AT mt_list ASSIGNING <lo_list>.
      IF <lo_list>->get_key( ) = iv_key.
        ro_repo = <lo_list>.
        RETURN.
      ENDIF.
    ENDLOOP.

    zcx_abapgit_exception=>raise( 'repo not found, get' ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~is_repo_installed.

    DATA: lt_repo        TYPE zif_abapgit_definitions=>ty_repo_ref_tt,
          lo_repo        TYPE REF TO zcl_abapgit_repo,
          lv_url         TYPE string,
          lv_package     TYPE devclass,
          lo_repo_online TYPE REF TO zcl_abapgit_repo_online,
          lv_err         TYPE string.

    lt_repo = list( ).

    LOOP AT lt_repo INTO lo_repo.
      CHECK lo_repo->is_offline( ) = abap_false.
      lo_repo_online ?= lo_repo.

      lv_url     = lo_repo_online->get_url( ).
      lv_package = lo_repo_online->get_package( ).
      CHECK to_upper( lv_url ) = to_upper( iv_url ).

      " Validate bindings
      "TODO refactor: move this message out of this method
      IF iv_target_package IS NOT INITIAL AND iv_target_package <> lv_package.
        lv_err = |Installation to package { lv_package } detected. |
              && |Cancelling installation|.
        zcx_abapgit_exception=>raise( lv_err ).
      ENDIF.

      rv_installed = abap_true.
      EXIT.
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~list.

    IF mv_init = abap_false.
      refresh( ).
    ENDIF.

    rt_list = mt_list.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~new_offline.

    DATA: ls_repo TYPE zif_abapgit_persistence=>ty_repo,
          lv_key  TYPE zif_abapgit_persistence=>ty_repo-key.

    validate_package( iv_package ).

    lv_key = mo_persistence->add(
      iv_url         = iv_url
      iv_branch_name = ''
      iv_package     = iv_package
      iv_offline     = abap_true
      is_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( )->get_data( ) ).

    TRY.
        ls_repo = mo_persistence->read( lv_key ).
      CATCH zcx_abapgit_not_found.
        zcx_abapgit_exception=>raise( 'new_offline not found' ).
    ENDTRY.

    CREATE OBJECT ro_repo
      EXPORTING
        is_data = ls_repo.

    add( ro_repo ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~new_online.

    DATA: ls_repo TYPE zif_abapgit_persistence=>ty_repo,
          lv_key  TYPE zif_abapgit_persistence=>ty_repo-key.

    ASSERT NOT iv_url IS INITIAL
      AND NOT iv_branch_name IS INITIAL
      AND NOT iv_package IS INITIAL.

    validate_package( iv_package ).
    zcl_abapgit_url=>validate( |{ iv_url }| ).

    lv_key = mo_persistence->add(
      iv_url         = iv_url
      iv_branch_name = iv_branch_name
      iv_package     = iv_package
      iv_offline     = abap_false
      is_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( )->get_data( ) ).
    TRY.
        ls_repo = mo_persistence->read( lv_key ).
      CATCH zcx_abapgit_not_found.
        zcx_abapgit_exception=>raise( 'new_online not found' ).
    ENDTRY.

    CREATE OBJECT ro_repo
      EXPORTING
        is_data = ls_repo.

    add( ro_repo ).

    ro_repo->refresh( ).
    ro_repo->find_remote_dot_abapgit( ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~purge.

* todo, this should be a method on the repo instead

    DATA: lt_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt.

    IF io_repo->get_local_settings( )-write_protected = abap_true.
      zcx_abapgit_exception=>raise( 'Cannot purge. Local code is write-protected by repo config' ).
    ELSEIF zcl_abapgit_auth=>is_allowed( zif_abapgit_auth=>gc_authorization-uninstall ) = abap_false.
      zcx_abapgit_exception=>raise( 'Not authorized' ).
    ENDIF.

    lt_tadir = zcl_abapgit_factory=>get_tadir( )->read( io_repo->get_package( ) ).

    zcl_abapgit_objects=>delete( it_tadir  = lt_tadir
                                 is_checks = is_checks ).

    delete( io_repo ).

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~switch_repo_type.

* todo, this should be a method on the repo instead?

    DATA lo_repo TYPE REF TO zcl_abapgit_repo.

    FIELD-SYMBOLS <lo_repo> LIKE LINE OF mt_list.

    lo_repo = get( iv_key ).
    READ TABLE mt_list ASSIGNING <lo_repo> FROM lo_repo.
    ASSERT sy-subrc IS INITIAL.
    ASSERT iv_offline <> lo_repo->ms_data-offline.

    IF iv_offline = abap_true. " On-line -> OFFline
      lo_repo->set(
        iv_url         = zcl_abapgit_url=>name( lo_repo->ms_data-url )
        iv_branch_name = ''
        iv_head_branch = ''
        iv_offline     = abap_true ).
      CREATE OBJECT <lo_repo> TYPE zcl_abapgit_repo_offline
        EXPORTING
          is_data = lo_repo->ms_data.
    ELSE. " OFFline -> On-line
      lo_repo->set( iv_offline = abap_false ).
      CREATE OBJECT <lo_repo> TYPE zcl_abapgit_repo_online
        EXPORTING
          is_data = lo_repo->ms_data.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_repo_srv~validate_package.

    DATA: lv_as4user TYPE tdevc-as4user,
          lt_repos   TYPE zif_abapgit_persistence=>tt_repo.

    IF iv_package IS INITIAL.
      zcx_abapgit_exception=>raise( 'add, package empty' ).
    ENDIF.

    IF iv_package = '$TMP'.
      zcx_abapgit_exception=>raise( 'not possible to use $TMP, create new (local) package' ).
    ENDIF.

    SELECT SINGLE as4user FROM tdevc
      INTO lv_as4user
      WHERE devclass = iv_package.                      "#EC CI_GENBUFF
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Package { iv_package } not found| ).
    ENDIF.

    IF is_sap_object_allowed( ) = abap_false AND lv_as4user = 'SAP'.
      zcx_abapgit_exception=>raise( |Package { iv_package } not allowed| ).
    ENDIF.

    " make sure its not already in use for a different repository
    lt_repos = mo_persistence->list( ).
    READ TABLE lt_repos WITH KEY package = iv_package TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Package { iv_package } already in use| ).
    ENDIF.

    validate_sub_super_packages(
      iv_package = iv_package
      it_repos   = lt_repos ).
  ENDMETHOD.
ENDCLASS.

validate packages: ignore sub packages if set

validate packages: ignore sub packages if set #1779