CLASS zcl_abapgit_branch_overview DEFINITION PUBLIC FINAL CREATE PRIVATE GLOBAL FRIENDS zcl_abapgit_factory . PUBLIC SECTION. INTERFACES zif_abapgit_branch_overview . CONSTANTS c_deleted_branch_name_prefix TYPE string VALUE '__DELETED_BRANCH_' ##NO_TEXT. METHODS constructor IMPORTING io_repo TYPE REF TO zcl_abapgit_repo_online RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. DATA mt_branches TYPE zif_abapgit_definitions=>ty_git_branch_list_tt . DATA mt_commits TYPE zif_abapgit_definitions=>ty_commit_tt . DATA mt_tags TYPE zif_abapgit_definitions=>ty_git_tag_list_tt . METHODS compress_internal IMPORTING !iv_name TYPE string CHANGING !ct_temp TYPE zif_abapgit_definitions=>ty_commit_tt !ct_commits TYPE zif_abapgit_definitions=>ty_commit_tt . METHODS parse_annotated_tags IMPORTING !it_objects TYPE zif_abapgit_definitions=>ty_objects_tt RAISING zcx_abapgit_exception . METHODS determine_branch RAISING zcx_abapgit_exception . METHODS determine_merges RAISING zcx_abapgit_exception . METHODS fixes RAISING zcx_abapgit_exception . METHODS get_git_objects IMPORTING !io_repo TYPE REF TO zcl_abapgit_repo_online RETURNING VALUE(rt_objects) TYPE zif_abapgit_definitions=>ty_objects_tt RAISING zcx_abapgit_exception . METHODS determine_tags RAISING zcx_abapgit_exception . METHODS get_deepen_level RETURNING VALUE(rv_result) TYPE i. ENDCLASS. CLASS zcl_abapgit_branch_overview IMPLEMENTATION. METHOD compress_internal. FIELD-SYMBOLS: LIKE LINE OF ct_temp, LIKE LINE OF ct_commits, LIKE LINE OF ct_temp. IF lines( ct_temp ) >= 10. READ TABLE ct_temp ASSIGNING INDEX 1. ASSERT sy-subrc = 0. READ TABLE ct_temp ASSIGNING INDEX lines( ct_temp ). ASSERT sy-subrc = 0. APPEND INITIAL LINE TO ct_commits ASSIGNING . -sha1 = -sha1. -parent1 = -parent1. -time = -time. -message = |Compressed, { lines( ct_temp ) } commits|. -branch = iv_name. -compressed = abap_true. ELSE. APPEND LINES OF ct_temp TO ct_commits. ENDIF. CLEAR ct_temp. ENDMETHOD. METHOD constructor. DATA: lt_objects TYPE zif_abapgit_definitions=>ty_objects_tt. lt_objects = get_git_objects( io_repo ). mt_commits = zcl_abapgit_git_commit=>parse_commits( lt_objects ). IF lines( mt_commits ) > 2000. zcx_abapgit_exception=>raise( 'Too many commits to display overview' ). ENDIF. zcl_abapgit_git_commit=>sort_commits( CHANGING ct_commits = mt_commits ). parse_annotated_tags( lt_objects ). CLEAR lt_objects. determine_branch( ). determine_merges( ). determine_tags( ). fixes( ). ENDMETHOD. METHOD determine_branch. CONSTANTS: lc_head TYPE string VALUE 'HEAD'. TYPES: BEGIN OF ty_branch_with_time, time TYPE string, name TYPE string, sha1 TYPE zif_abapgit_definitions=>ty_sha1, END OF ty_branch_with_time. DATA: lt_branches_sorted_by_time TYPE SORTED TABLE OF ty_branch_with_time WITH NON-UNIQUE KEY time, ls_branches_with_time TYPE ty_branch_with_time. FIELD-SYMBOLS: LIKE LINE OF mt_branches, LIKE LINE OF lt_branches_sorted_by_time, LIKE LINE OF mt_branches, LIKE LINE OF mt_commits, LIKE LINE OF -create. * Exchange HEAD, and make sure the branch determination starts with the HEAD branch READ TABLE mt_branches ASSIGNING WITH TABLE KEY name_key COMPONENTS name = lc_head. ASSERT sy-subrc = 0. LOOP AT mt_branches ASSIGNING WHERE sha1 = -sha1 AND name <> lc_head. -name = -name. DELETE mt_branches INDEX sy-tabix. EXIT. ENDLOOP. * Sort Branches by Commit Time LOOP AT mt_branches ASSIGNING . READ TABLE mt_commits ASSIGNING WITH KEY sha1 = -sha1. IF sy-subrc = 0. ls_branches_with_time-name = -name+11. ls_branches_with_time-sha1 = -sha1. IF -is_head = abap_true. ls_branches_with_time-time = '0000000000'. "Force HEAD to be the first one ELSE. ls_branches_with_time-time = -time. ENDIF. INSERT ls_branches_with_time INTO TABLE lt_branches_sorted_by_time. CLEAR ls_branches_with_time. ENDIF. ENDLOOP. LOOP AT lt_branches_sorted_by_time ASSIGNING . READ TABLE mt_commits ASSIGNING WITH KEY sha1 = -sha1. ASSERT sy-subrc = 0. DO. IF -branch IS INITIAL. -branch = -name. ELSE. APPEND INITIAL LINE TO -create ASSIGNING . -name = -name. -parent = -branch. EXIT. ENDIF. IF -parent1 IS INITIAL. EXIT. ELSE. READ TABLE mt_commits ASSIGNING WITH KEY sha1 = -parent1. ASSERT sy-subrc = 0. ENDIF. ENDDO. ENDLOOP. ENDMETHOD. METHOD determine_merges. DATA: BEGIN OF ls_deleted_branch_info, created TYPE abap_bool, index TYPE string, name TYPE string, END OF ls_deleted_branch_info. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_commit, TYPE zif_abapgit_definitions=>ty_commit, TYPE zif_abapgit_definitions=>ty_commit, TYPE zif_abapgit_definitions=>ty_create. * we need latest first here: latest -> initial zcl_abapgit_git_commit=>reverse_sort_order( CHANGING ct_commits = mt_commits ). LOOP AT mt_commits ASSIGNING WHERE NOT parent2 IS INITIAL. ASSERT NOT -branch IS INITIAL. READ TABLE mt_commits ASSIGNING WITH KEY sha1 = -parent2. IF sy-subrc = 0. -merge = -branch. * orphaned, branch has been deleted after merge ls_deleted_branch_info-created = abap_false. WHILE -branch IS INITIAL. IF ls_deleted_branch_info-created = abap_false. ls_deleted_branch_info-created = abap_true. ls_deleted_branch_info-index = ls_deleted_branch_info-index + 1. ls_deleted_branch_info-name = c_deleted_branch_name_prefix && ls_deleted_branch_info-index && '__'. CONDENSE ls_deleted_branch_info-name NO-GAPS. -merge = ls_deleted_branch_info-name. ENDIF. -branch = ls_deleted_branch_info-name. READ TABLE mt_commits ASSIGNING WITH KEY sha1 = -parent1. IF sy-subrc <> 0. EXIT. ELSE. ASSIGN TO . ENDIF. ENDWHILE. IF IS ASSIGNED. APPEND INITIAL LINE TO -create ASSIGNING . -name = ls_deleted_branch_info-name. -parent = -branch. ENDIF. ENDIF. ENDLOOP. " switch back to initial -> latest zcl_abapgit_git_commit=>reverse_sort_order( CHANGING ct_commits = mt_commits ). ENDMETHOD. METHOD determine_tags. DATA: lv_tag TYPE LINE OF zif_abapgit_definitions=>ty_commit-tags. FIELD-SYMBOLS: LIKE LINE OF mt_tags, LIKE LINE OF mt_commits. LOOP AT mt_tags ASSIGNING . IF -type = zif_abapgit_definitions=>c_git_branch_type-lightweight_tag. READ TABLE mt_commits WITH KEY sha1 = -sha1 ASSIGNING . "#EC CI_SUBRC ELSEIF -type = zif_abapgit_definitions=>c_git_branch_type-annotated_tag. READ TABLE mt_commits WITH KEY sha1 = -object ASSIGNING . ENDIF. CHECK sy-subrc = 0. lv_tag = zcl_abapgit_git_tag=>remove_tag_prefix( -name ). INSERT lv_tag INTO TABLE -tags. ENDLOOP. ENDMETHOD. METHOD fixes. FIELD-SYMBOLS: LIKE LINE OF mt_commits. LOOP AT mt_commits ASSIGNING WHERE NOT merge IS INITIAL. * commits from old branches IF -branch = -merge. CLEAR -merge. ENDIF. ENDLOOP. ENDMETHOD. METHOD get_git_objects. DATA: lo_branch_list TYPE REF TO zcl_abapgit_git_branch_list, li_progress TYPE REF TO zif_abapgit_progress, lt_branches_and_tags TYPE zif_abapgit_definitions=>ty_git_branch_list_tt, lt_tags TYPE zif_abapgit_definitions=>ty_git_branch_list_tt, ls_tag LIKE LINE OF mt_tags. FIELD-SYMBOLS: LIKE LINE OF lt_tags. li_progress = zcl_abapgit_progress=>get_instance( 1 ). li_progress->show( iv_current = 1 iv_text = |Get git objects { io_repo->get_name( ) }| ). * get objects directly from git, mo_repo only contains a shallow clone of only * the selected branch "TODO refactor lo_branch_list = zcl_abapgit_git_transport=>branches( io_repo->get_url( ) ). mt_branches = lo_branch_list->get_branches_only( ). INSERT LINES OF mt_branches INTO TABLE lt_branches_and_tags. lt_tags = lo_branch_list->get_tags_only( ). INSERT LINES OF lt_tags INTO TABLE lt_branches_and_tags. LOOP AT lt_tags ASSIGNING . IF -name CP '*^{}'. CONTINUE. ENDIF. MOVE-CORRESPONDING TO ls_tag. INSERT ls_tag INTO TABLE mt_tags. ENDLOOP. zcl_abapgit_git_transport=>upload_pack_by_branch( EXPORTING iv_url = io_repo->get_url( ) iv_branch_name = io_repo->get_selected_branch( ) iv_deepen_level = get_deepen_level( ) it_branches = lt_branches_and_tags IMPORTING et_objects = rt_objects ). DELETE rt_objects WHERE type = zif_abapgit_definitions=>c_type-blob. ENDMETHOD. METHOD parse_annotated_tags. DATA: ls_raw TYPE zcl_abapgit_git_pack=>ty_tag. FIELD-SYMBOLS: LIKE LINE OF it_objects, LIKE LINE OF mt_tags. LOOP AT it_objects ASSIGNING USING KEY type WHERE type = zif_abapgit_definitions=>c_type-tag. ls_raw = zcl_abapgit_git_pack=>decode_tag( -data ). READ TABLE mt_tags ASSIGNING WITH KEY sha1 = -sha1. ASSERT sy-subrc = 0. -name = zif_abapgit_definitions=>c_git_branch-tags_prefix && ls_raw-tag. -sha1 = -sha1. -object = ls_raw-object. -type = zif_abapgit_definitions=>c_git_branch_type-annotated_tag. -display_name = ls_raw-tag. -tagger_name = ls_raw-tagger_name. -tagger_email = ls_raw-tagger_email. -message = ls_raw-message. -body = ls_raw-body. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_branch_overview~compress. DATA: lv_previous TYPE i, lv_index TYPE i, lv_name TYPE string, lt_temp LIKE it_commits. FIELD-SYMBOLS: LIKE LINE OF mt_branches, LIKE LINE OF it_commits. LOOP AT mt_branches ASSIGNING . CLEAR lt_temp. lv_name = -name+11. LOOP AT it_commits ASSIGNING WHERE branch = lv_name. lv_index = sy-tabix. IF NOT -merge IS INITIAL OR NOT -create IS INITIAL. * always show these vertices lv_previous = -1. ENDIF. IF lv_previous + 1 <> sy-tabix. compress_internal( EXPORTING iv_name = lv_name CHANGING ct_temp = lt_temp ct_commits = rt_commits ). ENDIF. lv_previous = lv_index. APPEND TO lt_temp. ENDLOOP. compress_internal( EXPORTING iv_name = lv_name CHANGING ct_temp = lt_temp ct_commits = rt_commits ). ENDLOOP. zcl_abapgit_git_commit=>sort_commits( CHANGING ct_commits = rt_commits ). ENDMETHOD. METHOD zif_abapgit_branch_overview~get_branches. rt_branches = mt_branches. ENDMETHOD. METHOD zif_abapgit_branch_overview~get_commits. rt_commits = mt_commits. ENDMETHOD. METHOD zif_abapgit_branch_overview~get_tags. rt_tags = mt_tags. ENDMETHOD. METHOD get_deepen_level. DATA: lv_deepen_level(10) TYPE c. "Experimental: Use STVARV to get a locally configured value SELECT SINGLE low INTO lv_deepen_level FROM tvarvc WHERE name = 'ABAPGIT_TEST_LOG_LENGTH' ##WARN_OK. rv_result = lv_deepen_level. ENDMETHOD. ENDCLASS.