Possible solution to zabapgit merge functionality to resolve conflicts (#1407)

* Merging per Textfile

* Add files via upload

* Delete WolfTreak_ABAPGIT_Merging

* Refactoring vom Coding, Darstellung verbessert

* Merging page changed and also merging class extended to instance and conflict methods added

* Refactoring for Travis

* Refactoring for Travis

* Escaping of merged content functions now correct
This commit is contained in:
WolfTreak 2018-05-27 16:20:43 +02:00 committed by Lars Hvam
parent 174f9eafa7
commit 0aecdf7255
7 changed files with 985 additions and 144 deletions

View File

@ -1,31 +1,39 @@
CLASS zcl_abapgit_gui_page_merge DEFINITION CLASS zcl_abapgit_gui_page_merge DEFINITION
PUBLIC PUBLIC
INHERITING FROM zcl_abapgit_gui_page
FINAL FINAL
CREATE PUBLIC INHERITING FROM zcl_abapgit_gui_page. CREATE PUBLIC .
PUBLIC SECTION. PUBLIC SECTION.
METHODS:
constructor
IMPORTING io_repo TYPE REF TO zcl_abapgit_repo_online
iv_source TYPE string
iv_target TYPE string
RAISING zcx_abapgit_exception,
zif_abapgit_gui_page~on_event REDEFINITION.
METHODS constructor
IMPORTING
!io_repo TYPE REF TO zcl_abapgit_repo_online
!iv_source TYPE string
!iv_target TYPE string
RAISING
zcx_abapgit_exception .
METHODS zif_abapgit_gui_page~on_event
REDEFINITION.
PROTECTED SECTION. PROTECTED SECTION.
METHODS render_content REDEFINITION. METHODS render_content REDEFINITION.
PRIVATE SECTION. PRIVATE SECTION.
DATA: mo_repo TYPE REF TO zcl_abapgit_repo_online,
ms_merge TYPE zif_abapgit_definitions=>ty_merge.
CONSTANTS: BEGIN OF c_actions, DATA mo_repo TYPE REF TO zcl_abapgit_repo_online .
DATA mo_merge TYPE REF TO zcl_abapgit_merge .
CONSTANTS:
BEGIN OF c_actions,
merge TYPE string VALUE 'merge' ##NO_TEXT, merge TYPE string VALUE 'merge' ##NO_TEXT,
res_conflicts TYPE string VALUE 'res_conflicts' ##NO_TEXT,
END OF c_actions . END OF c_actions .
METHODS: METHODS build_menu
build_menu IMPORTING
RETURNING VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar. VALUE(iv_with_conflict) TYPE boolean OPTIONAL
RETURNING
VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar .
ENDCLASS. ENDCLASS.
@ -37,7 +45,11 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
CREATE OBJECT ro_menu. CREATE OBJECT ro_menu.
ro_menu->add( iv_txt = 'Merge' iv_act = c_actions-merge ) ##NO_TEXT. ro_menu->add( iv_txt = 'Merge' iv_act = c_actions-merge iv_cur = abap_false ) ##NO_TEXT.
IF iv_with_conflict EQ abap_true.
ro_menu->add( iv_txt = 'Resolve Conflicts' iv_act = c_actions-res_conflicts ) ##NO_TEXT.
ENDIF.
ENDMETHOD. ENDMETHOD.
@ -45,15 +57,18 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
METHOD constructor. METHOD constructor.
super->constructor( ). super->constructor( ).
ms_control-page_title = 'MERGE'.
ms_control-page_menu = build_menu( ).
mo_repo = io_repo. mo_repo = io_repo.
ms_merge = zcl_abapgit_merge=>run( CREATE OBJECT mo_merge
EXPORTING
io_repo = io_repo io_repo = io_repo
iv_source = iv_source iv_source_branch = iv_source
iv_target = iv_target ). iv_target_branch = iv_target.
mo_merge->run( ).
ms_control-page_title = 'MERGE'.
ms_control-page_menu = build_menu( iv_with_conflict = mo_merge->has_conflicts( ) ).
ENDMETHOD. ENDMETHOD.
@ -78,12 +93,17 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
ENDIF. ENDIF.
END-OF-DEFINITION. END-OF-DEFINITION.
DATA: lt_files LIKE ms_merge-stree, DATA: ls_merge TYPE zif_abapgit_definitions=>ty_merge,
ls_result LIKE LINE OF ms_merge-result. lt_files LIKE ls_merge-stree,
ls_result LIKE LINE OF ls_merge-result.
FIELD-SYMBOLS: <ls_show> LIKE LINE OF lt_files, FIELD-SYMBOLS: <ls_show> LIKE LINE OF lt_files,
<ls_file> LIKE LINE OF lt_files. <ls_file> LIKE LINE OF lt_files.
ls_merge = mo_merge->get_result( ).
"If now exists no conflicts anymore, conflicts button should disappear
ms_control-page_menu = build_menu( iv_with_conflict = mo_merge->has_conflicts( ) ).
CREATE OBJECT ro_html. CREATE OBJECT ro_html.
@ -95,27 +115,27 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
ro_html->add( '<table>' ). ro_html->add( '<table>' ).
ro_html->add( '<tr>' ). ro_html->add( '<tr>' ).
ro_html->add( '<td>Source:</td>' ). ro_html->add( '<td>Source</td>' ).
ro_html->add( '<td>' ). ro_html->add( '<td>' ).
ro_html->add( ms_merge-source-name ). ro_html->add( ls_merge-source-name ).
ro_html->add( '</td></tr>' ). ro_html->add( '</td></tr>' ).
ro_html->add( '<tr>' ). ro_html->add( '<tr>' ).
ro_html->add( '<td>Target:</td>' ). ro_html->add( '<td>Target</td>' ).
ro_html->add( '<td>' ). ro_html->add( '<td>' ).
ro_html->add( ms_merge-target-name ). ro_html->add( ls_merge-target-name ).
ro_html->add( '</td></tr>' ). ro_html->add( '</td></tr>' ).
ro_html->add( '<tr>' ). ro_html->add( '<tr>' ).
ro_html->add( '<td>Ancestor:</td>' ). ro_html->add( '<td>Ancestor</td>' ).
ro_html->add( '<td>' ). ro_html->add( '<td>' ).
ro_html->add( ms_merge-common-commit ). ro_html->add( ls_merge-common-commit ).
ro_html->add( '</td></tr>' ). ro_html->add( '</td></tr>' ).
ro_html->add( '</table>' ). ro_html->add( '</table>' ).
ro_html->add( '<br>' ). ro_html->add( '<br>' ).
APPEND LINES OF ms_merge-stree TO lt_files. APPEND LINES OF ls_merge-stree TO lt_files.
APPEND LINES OF ms_merge-ttree TO lt_files. APPEND LINES OF ls_merge-ttree TO lt_files.
APPEND LINES OF ms_merge-ctree TO lt_files. APPEND LINES OF ls_merge-ctree TO lt_files.
SORT lt_files BY path DESCENDING name ASCENDING. SORT lt_files BY path DESCENDING name ASCENDING.
DELETE ADJACENT DUPLICATES FROM lt_files COMPARING path name. DELETE ADJACENT DUPLICATES FROM lt_files COMPARING path name.
@ -132,20 +152,20 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
ro_html->add( '</tr>' ). ro_html->add( '</tr>' ).
LOOP AT lt_files ASSIGNING <ls_file>. LOOP AT lt_files ASSIGNING <ls_file>.
CLEAR ls_result. CLEAR ls_result.
READ TABLE ms_merge-result INTO ls_result READ TABLE ls_merge-result INTO ls_result
WITH KEY path = <ls_file>-path name = <ls_file>-name. WITH KEY path = <ls_file>-path name = <ls_file>-name.
ro_html->add( '<tr>' ). ro_html->add( '<tr>' ).
_show_file ms_merge-stree. _show_file ls_merge-stree.
_show_file ms_merge-ttree. _show_file ls_merge-ttree.
_show_file ms_merge-ctree. _show_file ls_merge-ctree.
_show_file ms_merge-result. _show_file ls_merge-result.
ro_html->add( '</tr>' ). ro_html->add( '</tr>' ).
ENDLOOP. ENDLOOP.
ro_html->add( '</table>' ). ro_html->add( '</table>' ).
ro_html->add( '<br>' ). ro_html->add( '<br>' ).
ro_html->add( '<b>' ). ro_html->add( '<b>' ).
ro_html->add( ms_merge-conflict ). ro_html->add( ls_merge-conflict ).
ro_html->add( '</b>' ). ro_html->add( '</b>' ).
ro_html->add( '</div>' ). ro_html->add( '</div>' ).
@ -156,15 +176,28 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE IMPLEMENTATION.
CASE iv_action. CASE iv_action.
WHEN c_actions-merge. WHEN c_actions-merge.
IF ms_merge-stage->count( ) = 0. IF mo_merge->has_conflicts( ) EQ abap_true.
zcx_abapgit_exception=>raise( 'conflicts exists' ).
ENDIF.
IF mo_merge->get_result( )-stage->count( ) EQ 0.
zcx_abapgit_exception=>raise( 'nothing to merge' ). zcx_abapgit_exception=>raise( 'nothing to merge' ).
ENDIF. ENDIF.
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit
EXPORTING EXPORTING
io_repo = mo_repo io_repo = mo_repo
io_stage = ms_merge-stage. io_stage = mo_merge->get_result( )-stage.
ev_state = zif_abapgit_definitions=>gc_event_state-new_page. ev_state = zif_abapgit_definitions=>gc_event_state-new_page.
WHEN c_actions-res_conflicts.
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_merge_res
EXPORTING
io_repo = mo_repo
io_merge_page = me
io_merge = mo_merge.
ev_state = zif_abapgit_definitions=>gc_event_state-new_page.
ENDCASE. ENDCASE.
ENDMETHOD. ENDMETHOD.

View File

@ -14,6 +14,14 @@
<FIXPT>X</FIXPT> <FIXPT>X</FIXPT>
<UNICODE>X</UNICODE> <UNICODE>X</UNICODE>
</VSEOCLASS> </VSEOCLASS>
<DESCRIPTIONS>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE</CLSNAME>
<CMPNAME>MO_MERGE</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>Merge logic</DESCRIPT>
</SEOCOMPOTX>
</DESCRIPTIONS>
</asx:values> </asx:values>
</asx:abap> </asx:abap>
</abapGit> </abapGit>

View File

@ -0,0 +1,583 @@
CLASS zcl_abapgit_gui_page_merge_res DEFINITION
PUBLIC
INHERITING FROM zcl_abapgit_gui_page
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
METHODS constructor
IMPORTING
!io_repo TYPE REF TO zcl_abapgit_repo_online
!io_merge_page TYPE REF TO zcl_abapgit_gui_page_merge
!io_merge TYPE REF TO zcl_abapgit_merge
RAISING
zcx_abapgit_exception .
METHODS zif_abapgit_gui_page~on_event
REDEFINITION .
PROTECTED SECTION.
METHODS render_content REDEFINITION.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_file_diff,
path TYPE string,
filename TYPE string,
lstate TYPE char1,
rstate TYPE char1,
fstate TYPE char1, " FILE state - Abstraction for shorter ifs
o_diff TYPE REF TO zcl_abapgit_diff,
changed_by TYPE xubname,
type TYPE string,
END OF ty_file_diff .
TYPES:
tt_file_diff TYPE STANDARD TABLE OF ty_file_diff .
CONSTANTS:
BEGIN OF c_actions,
toggle_mode TYPE string VALUE 'toggle_mode' ##NO_TEXT,
apply_merge TYPE string VALUE 'apply_merge' ##NO_TEXT,
apply_source TYPE string VALUE 'apply_source' ##NO_TEXT,
apply_target TYPE string VALUE 'apply_target' ##NO_TEXT,
cancel TYPE string VALUE 'cancel' ##NO_TEXT,
END OF c_actions .
CONSTANTS:
BEGIN OF c_merge_mode,
selection TYPE string VALUE 'selection' ##NO_TEXT,
merge TYPE string VALUE 'merge' ##NO_TEXT,
END OF c_merge_mode .
DATA mo_merge TYPE REF TO zcl_abapgit_merge .
DATA mo_merge_page TYPE REF TO zcl_abapgit_gui_page_merge .
DATA mo_repo TYPE REF TO zcl_abapgit_repo_online .
DATA ms_diff_file TYPE ty_file_diff .
DATA mv_current_conflict_index TYPE sytabix .
DATA mv_merge_mode TYPE string .
DATA mt_conflicts TYPE zif_abapgit_definitions=>tt_merge_conflict .
METHODS apply_merged_content
IMPORTING
!it_postdata TYPE cnht_post_data_tab
RAISING
zcx_abapgit_exception .
METHODS build_menu
IMPORTING
VALUE(iv_with_conflict) TYPE boolean OPTIONAL
RETURNING
VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar .
METHODS is_binary
IMPORTING
!iv_d1 TYPE xstring
!iv_d2 TYPE xstring
RETURNING
VALUE(rv_yes) TYPE abap_bool .
METHODS render_beacon
IMPORTING
!is_diff_line TYPE zif_abapgit_definitions=>ty_diff
!is_diff TYPE ty_file_diff
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html .
METHODS render_diff
IMPORTING
!is_diff TYPE ty_file_diff
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html
RAISING
zcx_abapgit_exception .
METHODS render_diff_head
IMPORTING
!is_diff TYPE ty_file_diff
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html .
METHODS render_lines
IMPORTING
!is_diff TYPE ty_file_diff
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html .
METHODS render_line_split
IMPORTING
!is_diff_line TYPE zif_abapgit_definitions=>ty_diff
!iv_fstate TYPE char1
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html .
METHODS render_table_head
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html .
METHODS resolve_diff
RAISING
zcx_abapgit_exception .
METHODS toggle_merge_mode .
ENDCLASS.
CLASS ZCL_ABAPGIT_GUI_PAGE_MERGE_RES IMPLEMENTATION.
METHOD apply_merged_content.
CONSTANTS: lc_replace TYPE string VALUE '<<new>>'.
DATA: BEGIN OF filedata,
merge_content TYPE string,
END OF filedata.
DATA: lv_string TYPE string,
lt_fields TYPE tihttpnvp,
lv_new_file_content TYPE xstring.
FIELD-SYMBOLS: <postdata_line> LIKE LINE OF it_postdata,
<ls_conflict> TYPE zif_abapgit_definitions=>ty_merge_conflict.
LOOP AT it_postdata ASSIGNING <postdata_line>.
lv_string = |{ lv_string }{ <postdata_line> }|.
ENDLOOP.
REPLACE ALL OCCURRENCES OF zif_abapgit_definitions=>gc_crlf IN lv_string WITH lc_replace.
REPLACE ALL OCCURRENCES OF zif_abapgit_definitions=>gc_newline IN lv_string WITH lc_replace.
lt_fields = zcl_abapgit_html_action_utils=>parse_fields_upper_case_name( lv_string ).
zcl_abapgit_html_action_utils=>get_field( EXPORTING name = 'MERGE_CONTENT'
it = lt_fields
CHANGING cv = filedata ).
filedata-merge_content = cl_http_utility=>unescape_url( escaped = filedata-merge_content ).
REPLACE ALL OCCURRENCES OF lc_replace IN filedata-merge_content WITH zif_abapgit_definitions=>gc_newline.
lv_new_file_content = zcl_abapgit_convert=>string_to_xstring_utf8( iv_string = filedata-merge_content ).
READ TABLE mt_conflicts ASSIGNING <ls_conflict> INDEX mv_current_conflict_index.
<ls_conflict>-result_sha1 = zcl_abapgit_hash=>sha1( iv_type = zif_abapgit_definitions=>gc_type-blob
iv_data = lv_new_file_content ).
<ls_conflict>-result_data = lv_new_file_content.
mo_merge->resolve_conflict( is_conflict = <ls_conflict> ).
ENDMETHOD.
METHOD build_menu.
CREATE OBJECT ro_menu.
ro_menu->add( iv_txt = 'Toggle merge mode' iv_act = c_actions-toggle_mode ) ##NO_TEXT.
ro_menu->add( iv_txt = 'Cancel' iv_act = c_actions-cancel ) ##NO_TEXT.
ENDMETHOD.
METHOD constructor.
super->constructor( ).
mo_repo = io_repo.
ms_control-page_title = 'Resolve Conflicts'.
ms_control-page_menu = build_menu( ).
mo_merge_page = io_merge_page.
mo_merge = io_merge.
mv_merge_mode = c_merge_mode-selection.
mv_current_conflict_index = 1.
mt_conflicts = io_merge->get_conflicts( ).
ENDMETHOD.
METHOD is_binary.
DATA: lv_len TYPE i,
lv_idx TYPE i,
lv_x TYPE x.
FIELD-SYMBOLS <lv_data> LIKE iv_d1.
IF iv_d1 IS NOT INITIAL. " One of them might be new and so empty
ASSIGN iv_d1 TO <lv_data>.
ELSE.
ASSIGN iv_d2 TO <lv_data>.
ENDIF.
lv_len = xstrlen( <lv_data> ).
IF lv_len = 0.
RETURN.
ENDIF.
IF lv_len > 100.
lv_len = 100.
ENDIF.
" Simple char range test
" stackoverflow.com/questions/277521/how-to-identify-the-file-content-as-ascii-or-binary
DO lv_len TIMES. " I'm sure there is more efficient way ...
lv_idx = sy-index - 1.
lv_x = <lv_data>+lv_idx(1).
IF NOT ( lv_x BETWEEN 9 AND 13 OR lv_x BETWEEN 32 AND 126 ).
rv_yes = abap_true.
EXIT.
ENDIF.
ENDDO.
ENDMETHOD. " is_binary.
METHOD render_beacon.
DATA: lv_beacon TYPE string.
CREATE OBJECT ro_html.
IF is_diff_line-beacon > 0.
READ TABLE is_diff-o_diff->mt_beacons INTO lv_beacon INDEX is_diff_line-beacon.
ELSE.
lv_beacon = '---'.
ENDIF.
ro_html->add( '<thead class="nav_line">' ).
ro_html->add( '<tr>' ).
ro_html->add( '<th class="num"></th>' ).
ro_html->add( |<th colspan="3">@@ { is_diff_line-new_num } @@ { lv_beacon }</th>| ).
ro_html->add( '</tr>' ).
ro_html->add( '</thead>' ).
ENDMETHOD. " render_beacon.
METHOD render_content.
resolve_diff( ).
IF ms_diff_file IS INITIAL.
zcx_abapgit_exception=>raise( 'no conflict found' ).
ENDIF.
CREATE OBJECT ro_html.
ro_html->add( |<div id="diff-list" data-repo-key="{ mo_repo->get_key( ) }">| ).
ro_html->add( render_diff( ms_diff_file ) ).
ro_html->add( '</div>' ).
ENDMETHOD. "render_content
METHOD render_diff.
DATA: lv_target_content TYPE string.
FIELD-SYMBOLS: <ls_conflict> TYPE zif_abapgit_definitions=>ty_merge_conflict.
CREATE OBJECT ro_html.
ro_html->add( |<div class="diff" data-type="{ is_diff-type
}" data-changed-by="{ is_diff-changed_by
}" data-file="{ is_diff-path && is_diff-filename }">| ). "#EC NOTEXT
ro_html->add( render_diff_head( is_diff ) ).
" Content
IF is_diff-type <> 'binary'.
IF mv_merge_mode EQ c_merge_mode-selection.
ro_html->add( '<div class="diff_content">' ). "#EC NOTEXT
ro_html->add( '<table class="diff_tab syntax-hl">' ). "#EC NOTEXT
ro_html->add( render_table_head( ) ).
ro_html->add( render_lines( is_diff ) ).
ro_html->add( '</table>' ). "#EC NOTEXT
ro_html->add( '</div>' ). "#EC NOTEXT
ELSE.
"Table for Div-Table and textarea
ro_html->add( '<div class="diff_content">' ). "#EC NOTEXT
ro_html->add( '<table>' ). "#EC NOTEXT
ro_html->add( '<thead class="header">' ). "#EC NOTEXT
ro_html->add( '<tr>' ). "#EC NOTEXT
ro_html->add( '<th>Code</th>' ). "#EC NOTEXT
ro_html->add( '<th>Merge - ' ). "#EC NOTEXT
ro_html->add_a( iv_act = 'submitFormById(''merge_form'');' "#EC NOTEXT
iv_txt = 'Apply'
iv_typ = zif_abapgit_definitions=>gc_action_type-onclick
iv_opt = zif_abapgit_definitions=>gc_html_opt-strong ).
ro_html->add( '</th> ' ). "#EC NOTEXT
ro_html->add( '</tr>' ). "#EC NOTEXT
ro_html->add( '</thead>' ). "#EC NOTEXT
ro_html->add( '<td>' ).
"Diff-Table of source and target file
ro_html->add( '<table class="diff_tab syntax-hl">' ). "#EC NOTEXT
ro_html->add( render_table_head( ) ).
ro_html->add( render_lines( is_diff ) ).
ro_html->add( '</table>' ). "#EC NOTEXT
READ TABLE mt_conflicts ASSIGNING <ls_conflict> INDEX mv_current_conflict_index.
IF sy-subrc EQ 0.
lv_target_content = zcl_abapgit_convert=>xstring_to_string_utf8( <ls_conflict>-target_data ).
lv_target_content = escape( val = lv_target_content format = cl_abap_format=>e_html_text ).
ENDIF.
ro_html->add( '</td>' ). "#EC NOTEXT
ro_html->add( '<td>' ). "#EC NOTEXT
ro_html->add( '<div class="form-container">' ).
ro_html->add( |<form id="merge_form" class="aligned-form" accept-charset="UTF-8"| ).
ro_html->add( |method="post" action="sapevent:apply_merge">| ).
ro_html->add( |<textarea id="merge_content" name="merge_content" | ).
ro_html->add( |rows="{ lines( is_diff-o_diff->get( ) ) }">{ lv_target_content }</textarea>| ).
ro_html->add( '<input type="submit" class="hidden-submit">' ).
ro_html->add( '</form>' ). "#EC NOTEXT
ro_html->add( '</div>' ). "#EC NOTEXT
ro_html->add( '</td>' ). "#EC NOTEXT
ro_html->add( '</table>' ). "#EC NOTEXT
ro_html->add( '</div>' ). "#EC NOTEXT
ENDIF.
ELSE.
ro_html->add( '<div class="diff_content paddings center grey">' ). "#EC NOTEXT
ro_html->add( 'The content seems to be binary.' ). "#EC NOTEXT
ro_html->add( 'Cannot display as diff.' ). "#EC NOTEXT
ro_html->add( '</div>' ). "#EC NOTEXT
ENDIF.
ro_html->add( '</div>' ). "#EC NOTEXT
ENDMETHOD. " render_diff
METHOD render_diff_head.
DATA: ls_stats TYPE zif_abapgit_definitions=>ty_count.
CREATE OBJECT ro_html.
ro_html->add( '<div class="diff_head">' ). "#EC NOTEXT
IF is_diff-type <> 'binary' AND is_diff-o_diff IS NOT INITIAL.
ls_stats = is_diff-o_diff->stats( ).
ro_html->add( |<span class="diff_banner diff_ins">+ { ls_stats-insert }</span>| ).
ro_html->add( |<span class="diff_banner diff_del">- { ls_stats-delete }</span>| ).
ro_html->add( |<span class="diff_banner diff_upd">~ { ls_stats-update }</span>| ).
ENDIF.
ro_html->add( |<span class="diff_name">{ is_diff-filename }</span>| ). "#EC NOTEXT
ro_html->add( '</div>' ). "#EC NOTEXT
ENDMETHOD.
METHOD render_lines.
DATA: lo_highlighter TYPE REF TO zcl_abapgit_syntax_highlighter,
lt_diffs TYPE zif_abapgit_definitions=>ty_diffs_tt,
lv_insert_nav TYPE abap_bool.
FIELD-SYMBOLS <ls_diff> LIKE LINE OF lt_diffs.
lo_highlighter = zcl_abapgit_syntax_highlighter=>create( is_diff-filename ).
CREATE OBJECT ro_html.
lt_diffs = is_diff-o_diff->get( ).
LOOP AT lt_diffs ASSIGNING <ls_diff>.
IF <ls_diff>-short = abap_false.
lv_insert_nav = abap_true.
CONTINUE.
ENDIF.
IF lv_insert_nav = abap_true. " Insert separator line with navigation
ro_html->add( render_beacon( is_diff_line = <ls_diff> is_diff = is_diff ) ).
lv_insert_nav = abap_false.
ENDIF.
IF lo_highlighter IS BOUND.
<ls_diff>-new = lo_highlighter->process_line( <ls_diff>-new ).
<ls_diff>-old = lo_highlighter->process_line( <ls_diff>-old ).
ELSE.
<ls_diff>-new = escape( val = <ls_diff>-new format = cl_abap_format=>e_html_attr ).
<ls_diff>-old = escape( val = <ls_diff>-old format = cl_abap_format=>e_html_attr ).
ENDIF.
CONDENSE <ls_diff>-new_num. "get rid of leading spaces
CONDENSE <ls_diff>-old_num.
ro_html->add( render_line_split( is_diff_line = <ls_diff>
iv_fstate = is_diff-fstate ) ).
ENDLOOP.
ENDMETHOD. "render_lines
METHOD render_line_split.
DATA: lv_new TYPE string,
lv_old TYPE string,
lv_merge TYPE string,
lv_mark TYPE string,
lv_bg TYPE string.
CREATE OBJECT ro_html.
" New line
lv_mark = ` `.
IF is_diff_line-result = zif_abapgit_definitions=>c_diff-update.
lv_bg = ' diff_upd'.
lv_mark = `~`.
ELSEIF is_diff_line-result = zif_abapgit_definitions=>c_diff-insert.
lv_bg = ' diff_ins'.
lv_mark = `+`.
ENDIF.
lv_new = |<td class="num" line-num="{ is_diff_line-new_num }"></td>|
&& |<td class="code{ lv_bg }">{ lv_mark }{ is_diff_line-new }</td>|.
" Old line
CLEAR lv_bg.
lv_mark = ` `.
IF is_diff_line-result = zif_abapgit_definitions=>c_diff-update.
lv_bg = ' diff_upd'.
lv_mark = `~`.
ELSEIF is_diff_line-result = zif_abapgit_definitions=>c_diff-delete.
lv_bg = ' diff_del'.
lv_mark = `-`.
ENDIF.
lv_old = |<td class="num" line-num="{ is_diff_line-old_num }"></td>|
&& |<td class="code{ lv_bg }">{ lv_mark }{ is_diff_line-old }</td>|.
" render line, inverse sides if remote is newer
ro_html->add( '<tr>' ). "#EC NOTEXT
ro_html->add( lv_old ). " Target
ro_html->add( lv_new ). " Source
ro_html->add( '</tr>' ). "#EC NOTEXT
ENDMETHOD. "render_line_split
METHOD render_table_head.
CREATE OBJECT ro_html.
IF mv_merge_mode EQ c_merge_mode-selection.
ro_html->add( '<thead class="header">' ). "#EC NOTEXT
ro_html->add( '<tr>' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<form id="target_form" method="post" action="sapevent:apply_target">' ). "#EC NOTEXT
ro_html->add( '<th>Target - ' && mo_merge->get_target_branch( ) && ' - ' ). "#EC NOTEXT
ro_html->add_a( iv_act = 'submitFormById(''target_form'');' "#EC NOTEXT
iv_txt = 'Apply'
iv_typ = zif_abapgit_definitions=>gc_action_type-onclick
iv_opt = zif_abapgit_definitions=>gc_html_opt-strong ).
ro_html->add( '</th> ' ). "#EC NOTEXT
ro_html->add( '</form>' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<form id="source_form" method="post" action="sapevent:apply_source">' ). "#EC NOTEXT
ro_html->add( '<th>Source - ' && mo_merge->get_source_branch( ) &&' - ' ). "#EC NOTEXT
ro_html->add_a( iv_act = 'submitFormById(''source_form'');' "#EC NOTEXT
iv_txt = 'Apply'
iv_typ = zif_abapgit_definitions=>gc_action_type-onclick
iv_opt = zif_abapgit_definitions=>gc_html_opt-strong ).
ro_html->add( '</th> ' ). "#EC NOTEXT
ro_html->add( '</form>' ). "#EC NOTEXT
ro_html->add( '</tr>' ). "#EC NOTEXT
ro_html->add( '</thead>' ). "#EC NOTEXT
ELSE.
ro_html->add( '<thead class="header">' ). "#EC NOTEXT
ro_html->add( '<tr>' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>Target - ' && mo_merge->get_target_branch( ) &&'</th> ' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>Source - ' && mo_merge->get_source_branch( ) &&'</th> ' ). "#EC NOTEXT
ro_html->add( '</tr>' ). "#EC NOTEXT
ro_html->add( '</thead>' ). "#EC NOTEXT
ENDIF.
ENDMETHOD. " render_table_head.
METHOD resolve_diff.
DATA: lv_offs TYPE i.
FIELD-SYMBOLS: <ls_conflict> TYPE zif_abapgit_definitions=>ty_merge_conflict.
CLEAR ms_diff_file.
READ TABLE mt_conflicts ASSIGNING <ls_conflict> INDEX mv_current_conflict_index.
IF sy-subrc NE 0.
RETURN.
ENDIF.
ms_diff_file-path = <ls_conflict>-path.
ms_diff_file-filename = <ls_conflict>-filename.
ms_diff_file-type = reverse( <ls_conflict>-filename ).
FIND FIRST OCCURRENCE OF '.' IN ms_diff_file-type MATCH OFFSET lv_offs.
ms_diff_file-type = reverse( substring( val = ms_diff_file-type len = lv_offs ) ).
IF ms_diff_file-type <> 'xml' AND ms_diff_file-type <> 'abap'.
ms_diff_file-type = 'other'.
ENDIF.
IF ms_diff_file-type = 'other'
AND is_binary( iv_d1 = <ls_conflict>-source_data iv_d2 = <ls_conflict>-target_data ) = abap_true.
ms_diff_file-type = 'binary'.
ENDIF.
IF ms_diff_file-type <> 'binary'.
CREATE OBJECT ms_diff_file-o_diff
EXPORTING
iv_new = <ls_conflict>-source_data
iv_old = <ls_conflict>-target_data.
ENDIF.
ENDMETHOD.
METHOD toggle_merge_mode.
IF mv_merge_mode EQ c_merge_mode-selection.
mv_merge_mode = c_merge_mode-merge.
ELSE.
mv_merge_mode = c_merge_mode-selection.
ENDIF.
ENDMETHOD.
METHOD zif_abapgit_gui_page~on_event.
FIELD-SYMBOLS: <ls_conflict> TYPE zif_abapgit_definitions=>ty_merge_conflict.
CASE iv_action.
WHEN c_actions-apply_merge
OR c_actions-apply_source
OR c_actions-apply_target
OR c_actions-cancel.
CASE iv_action.
WHEN c_actions-apply_merge.
apply_merged_content( it_postdata = it_postdata ).
WHEN c_actions-apply_source.
READ TABLE mt_conflicts ASSIGNING <ls_conflict> INDEX mv_current_conflict_index.
<ls_conflict>-result_sha1 = <ls_conflict>-source_sha1.
<ls_conflict>-result_data = <ls_conflict>-source_data.
mo_merge->resolve_conflict( is_conflict = <ls_conflict> ).
WHEN c_actions-apply_target.
READ TABLE mt_conflicts ASSIGNING <ls_conflict> INDEX mv_current_conflict_index.
<ls_conflict>-result_sha1 = <ls_conflict>-target_sha1.
<ls_conflict>-result_data = <ls_conflict>-target_data.
mo_merge->resolve_conflict( is_conflict = <ls_conflict> ).
ENDCASE.
mv_current_conflict_index = mv_current_conflict_index + 1.
IF mv_current_conflict_index > lines( mt_conflicts ).
CLEAR mv_current_conflict_index.
ENDIF.
IF mv_current_conflict_index IS NOT INITIAL.
ev_state = zif_abapgit_definitions=>gc_event_state-re_render.
ELSE.
ei_page = mo_merge_page.
ev_state = zif_abapgit_definitions=>gc_event_state-go_back.
ENDIF.
WHEN c_actions-toggle_mode.
toggle_merge_mode( ).
ev_state = zif_abapgit_definitions=>gc_event_state-re_render.
ENDCASE.
ENDMETHOD.
ENDCLASS.

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE_RES</CLSNAME>
<VERSION>1</VERSION>
<LANGU>E</LANGU>
<DESCRIPT>GUI - Resolving Conflicts page</DESCRIPT>
<EXPOSURE>2</EXPOSURE>
<STATE>1</STATE>
<CLSFINAL>X</CLSFINAL>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
</VSEOCLASS>
<DESCRIPTIONS>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE_RES</CLSNAME>
<CMPNAME>MO_MERGE</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>Merge logic</DESCRIPT>
</SEOCOMPOTX>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE_RES</CLSNAME>
<CMPNAME>MO_MERGE_PAGE</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>GUI - Merging page</DESCRIPT>
</SEOCOMPOTX>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE_RES</CLSNAME>
<CMPNAME>MO_REPO</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>Online Repository</DESCRIPT>
</SEOCOMPOTX>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_GUI_PAGE_MERGE_RES</CLSNAME>
<CMPNAME>MV_CURRENT_CONFLICT_INDEX</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>Row Index of Internal Tables</DESCRIPT>
</SEOCOMPOTX>
</DESCRIPTIONS>
</asx:values>
</asx:abap>
</abapGit>

View File

@ -1,37 +1,78 @@
CLASS zcl_abapgit_merge DEFINITION PUBLIC FINAL CREATE PUBLIC. CLASS zcl_abapgit_merge DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION. PUBLIC SECTION.
CLASS-METHODS:
run METHODS constructor
IMPORTING io_repo TYPE REF TO zcl_abapgit_repo_online IMPORTING
iv_source TYPE string !io_repo TYPE REF TO zcl_abapgit_repo_online
iv_target TYPE string !iv_source_branch TYPE string
RETURNING VALUE(rs_merge) TYPE zif_abapgit_definitions=>ty_merge !iv_target_branch TYPE string
RAISING zcx_abapgit_exception. RAISING
zcx_abapgit_exception .
METHODS get_conflicts
RETURNING
VALUE(rt_conflicts) TYPE zif_abapgit_definitions=>tt_merge_conflict .
METHODS get_result
RETURNING
VALUE(rs_merge) TYPE zif_abapgit_definitions=>ty_merge .
METHODS get_source_branch
RETURNING
VALUE(rv_source_branch) TYPE string .
METHODS get_target_branch
RETURNING
VALUE(rv_target_branch) TYPE string .
METHODS has_conflicts
RETURNING
VALUE(rv_conflicts_exists) TYPE boolean .
METHODS resolve_conflict
IMPORTING
!is_conflict TYPE zif_abapgit_definitions=>ty_merge_conflict
RAISING
zcx_abapgit_exception .
METHODS run
RAISING
zcx_abapgit_exception .
PRIVATE SECTION. PRIVATE SECTION.
CLASS-DATA: gs_merge TYPE zif_abapgit_definitions=>ty_merge,
gt_objects TYPE zif_abapgit_definitions=>ty_objects_tt.
TYPES: ty_ancestor_tt TYPE STANDARD TABLE OF zif_abapgit_definitions=>ty_ancestor WITH DEFAULT KEY. TYPES:
ty_ancestor_tt TYPE STANDARD TABLE OF zif_abapgit_definitions=>ty_ancestor WITH DEFAULT KEY .
CLASS-METHODS: DATA mo_repo TYPE REF TO zcl_abapgit_repo_online .
all_files DATA ms_merge TYPE zif_abapgit_definitions=>ty_merge .
RETURNING VALUE(rt_files) TYPE zif_abapgit_definitions=>ty_expanded_tt, DATA mt_conflicts TYPE zif_abapgit_definitions=>tt_merge_conflict .
calculate_result DATA mt_objects TYPE zif_abapgit_definitions=>ty_objects_tt .
RAISING zcx_abapgit_exception, DATA mv_source_branch TYPE string .
find_ancestors DATA mv_target_branch TYPE string .
IMPORTING iv_commit TYPE zif_abapgit_definitions=>ty_sha1
RETURNING VALUE(rt_ancestors) TYPE ty_ancestor_tt METHODS all_files
RAISING zcx_abapgit_exception, RETURNING
find_first_common VALUE(rt_files) TYPE zif_abapgit_definitions=>ty_expanded_tt .
IMPORTING it_list1 TYPE ty_ancestor_tt METHODS calculate_result
it_list2 TYPE ty_ancestor_tt RAISING
RETURNING VALUE(rs_common) TYPE zif_abapgit_definitions=>ty_ancestor zcx_abapgit_exception .
RAISING zcx_abapgit_exception, METHODS fetch_git
fetch_git RETURNING
IMPORTING iv_source TYPE string VALUE(rt_objects) TYPE zif_abapgit_definitions=>ty_objects_tt
iv_target TYPE string RAISING
RAISING zcx_abapgit_exception. zcx_abapgit_exception .
METHODS find_ancestors
IMPORTING
!iv_commit TYPE zif_abapgit_definitions=>ty_sha1
RETURNING
VALUE(rt_ancestors) TYPE ty_ancestor_tt
RAISING
zcx_abapgit_exception .
METHODS find_first_common
IMPORTING
!it_list1 TYPE ty_ancestor_tt
!it_list2 TYPE ty_ancestor_tt
RETURNING
VALUE(rs_common) TYPE zif_abapgit_definitions=>ty_ancestor
RAISING
zcx_abapgit_exception .
ENDCLASS. ENDCLASS.
@ -41,9 +82,9 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
METHOD all_files. METHOD all_files.
APPEND LINES OF gs_merge-stree TO rt_files. APPEND LINES OF ms_merge-stree TO rt_files.
APPEND LINES OF gs_merge-ttree TO rt_files. APPEND LINES OF ms_merge-ttree TO rt_files.
APPEND LINES OF gs_merge-ctree TO rt_files. APPEND LINES OF ms_merge-ctree TO rt_files.
SORT rt_files BY path DESCENDING name ASCENDING. SORT rt_files BY path DESCENDING name ASCENDING.
DELETE ADJACENT DUPLICATES FROM rt_files COMPARING path name. DELETE ADJACENT DUPLICATES FROM rt_files COMPARING path name.
@ -53,12 +94,12 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
METHOD calculate_result. METHOD calculate_result.
DEFINE _from_source. DEFINE _from_source.
READ TABLE gt_objects ASSIGNING <ls_object> READ TABLE mt_objects ASSIGNING <ls_object>
WITH KEY type = zif_abapgit_definitions=>gc_type-blob WITH KEY type = zif_abapgit_definitions=>gc_type-blob
sha1 = <ls_source>-sha1. sha1 = <ls_source>-sha1.
ASSERT sy-subrc = 0. ASSERT sy-subrc = 0.
gs_merge-stage->add( iv_path = <ls_file>-path ms_merge-stage->add( iv_path = <ls_file>-path
iv_filename = <ls_file>-name iv_filename = <ls_file>-name
iv_data = <ls_object>-data ). iv_data = <ls_object>-data ).
END-OF-DEFINITION. END-OF-DEFINITION.
@ -72,17 +113,17 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
<ls_target> LIKE LINE OF lt_files, <ls_target> LIKE LINE OF lt_files,
<ls_common> LIKE LINE OF lt_files, <ls_common> LIKE LINE OF lt_files,
<ls_file> LIKE LINE OF lt_files, <ls_file> LIKE LINE OF lt_files,
<ls_result> LIKE LINE OF gs_merge-result, <ls_result> LIKE LINE OF ms_merge-result,
<ls_object> LIKE LINE OF gt_objects. <ls_object> LIKE LINE OF mt_objects,
<ls_conflict> LIKE LINE OF mt_conflicts.
lt_files = all_files( ). lt_files = all_files( ).
CREATE OBJECT gs_merge-stage CREATE OBJECT ms_merge-stage
EXPORTING EXPORTING
iv_branch_name = gs_merge-target-name iv_branch_name = ms_merge-target-name
iv_branch_sha1 = gs_merge-target-sha1 iv_branch_sha1 = ms_merge-target-sha1
iv_merge_source = gs_merge-source-sha1. iv_merge_source = ms_merge-source-sha1.
LOOP AT lt_files ASSIGNING <ls_file>. LOOP AT lt_files ASSIGNING <ls_file>.
@ -90,11 +131,11 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
UNASSIGN <ls_target>. UNASSIGN <ls_target>.
UNASSIGN <ls_common>. UNASSIGN <ls_common>.
READ TABLE gs_merge-stree ASSIGNING <ls_source> READ TABLE ms_merge-stree ASSIGNING <ls_source>
WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC
READ TABLE gs_merge-ttree ASSIGNING <ls_target> READ TABLE ms_merge-ttree ASSIGNING <ls_target>
WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC
READ TABLE gs_merge-ctree ASSIGNING <ls_common> READ TABLE ms_merge-ctree ASSIGNING <ls_common>
WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC WITH KEY path = <ls_file>-path name = <ls_file>-name. "#EC CI_SUBRC
lv_found_source = boolc( <ls_source> IS ASSIGNED ). lv_found_source = boolc( <ls_source> IS ASSIGNED ).
@ -109,7 +150,7 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
AND lv_found_common = abap_true AND lv_found_common = abap_true
AND <ls_target>-sha1 = <ls_common>-sha1. AND <ls_target>-sha1 = <ls_common>-sha1.
* deleted in source, skip * deleted in source, skip
gs_merge-stage->rm( iv_path = <ls_file>-path ms_merge-stage->rm( iv_path = <ls_file>-path
iv_filename = <ls_file>-name ). iv_filename = <ls_file>-name ).
CONTINUE. CONTINUE.
ELSEIF lv_found_target = abap_false ELSEIF lv_found_target = abap_false
@ -119,7 +160,7 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
CONTINUE. CONTINUE.
ENDIF. ENDIF.
APPEND INITIAL LINE TO gs_merge-result ASSIGNING <ls_result>. APPEND INITIAL LINE TO ms_merge-result ASSIGNING <ls_result>.
<ls_result>-path = <ls_file>-path. <ls_result>-path = <ls_file>-path.
<ls_result>-name = <ls_file>-name. <ls_result>-name = <ls_file>-name.
@ -135,15 +176,32 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
AND <ls_target>-sha1 = <ls_source>-sha1. AND <ls_target>-sha1 = <ls_source>-sha1.
* added in source and target * added in source and target
<ls_result>-sha1 = <ls_source>-sha1. <ls_result>-sha1 = <ls_source>-sha1.
ELSEIF lv_found_common = abap_false
AND <ls_target>-sha1 <> <ls_source>-sha1.
INSERT INITIAL LINE INTO TABLE mt_conflicts ASSIGNING <ls_conflict>.
<ls_conflict>-path = <ls_file>-path.
<ls_conflict>-filename = <ls_file>-name.
<ls_conflict>-source_sha1 = <ls_source>-sha1.
READ TABLE mt_objects ASSIGNING <ls_object> WITH KEY type = zif_abapgit_definitions=>gc_type-blob
sha1 = <ls_source>-sha1.
<ls_conflict>-source_data = <ls_object>-data.
<ls_conflict>-target_sha1 = <ls_target>-sha1.
READ TABLE mt_objects ASSIGNING <ls_object> WITH KEY type = zif_abapgit_definitions=>gc_type-blob
sha1 = <ls_target>-sha1.
<ls_conflict>-target_data = <ls_object>-data.
* added in source and target, but different, merge conflict must be resolved
ms_merge-conflict = |{ <ls_file>-name } merge conflict|.
CONTINUE.
ENDIF. ENDIF.
IF lv_found_source = abap_false IF lv_found_source = abap_false
OR lv_found_target = abap_false OR lv_found_target = abap_false
OR lv_found_common = abap_false. OR lv_found_common = abap_false.
CLEAR gs_merge-result. ms_merge-conflict = |{ <ls_file>-name } merge conflict, not found anywhere|.
gs_merge-conflict = |{ <ls_file>-name CONTINUE.
} merge conflict, not found anywhere|.
RETURN.
ENDIF. ENDIF.
IF <ls_target>-sha1 = <ls_source>-sha1. IF <ls_target>-sha1 = <ls_source>-sha1.
@ -158,13 +216,36 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
<ls_result>-sha1 = <ls_target>-sha1. <ls_result>-sha1 = <ls_target>-sha1.
ELSE. ELSE.
* changed in source and target, conflict * changed in source and target, conflict
CLEAR gs_merge-result. * conflict must be resolved before merge
gs_merge-conflict = |{ <ls_file>-name INSERT INITIAL LINE INTO TABLE mt_conflicts ASSIGNING <ls_conflict>.
} merge conflict, changed in source and target branch|. <ls_conflict>-path = <ls_file>-path.
RETURN. <ls_conflict>-filename = <ls_file>-name.
<ls_conflict>-source_sha1 = <ls_source>-sha1.
READ TABLE mt_objects ASSIGNING <ls_object> WITH KEY type = zif_abapgit_definitions=>gc_type-blob
sha1 = <ls_source>-sha1.
<ls_conflict>-source_data = <ls_object>-data.
<ls_conflict>-target_sha1 = <ls_target>-sha1.
READ TABLE mt_objects ASSIGNING <ls_object> WITH KEY type = zif_abapgit_definitions=>gc_type-blob
sha1 = <ls_target>-sha1.
<ls_conflict>-target_data = <ls_object>-data.
ms_merge-conflict = |{ <ls_file>-name } merge conflict, changed in source and target branch|.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD constructor.
IF iv_source_branch EQ iv_target_branch.
zcx_abapgit_exception=>raise( 'source = target' ).
ENDIF. ENDIF.
ENDLOOP. mo_repo = io_repo.
mv_source_branch = iv_source_branch.
mv_target_branch = iv_target_branch.
ENDMETHOD. ENDMETHOD.
@ -174,24 +255,23 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
DATA: lo_branch_list TYPE REF TO zcl_abapgit_git_branch_list, DATA: lo_branch_list TYPE REF TO zcl_abapgit_git_branch_list,
lt_upload TYPE zif_abapgit_definitions=>ty_git_branch_list_tt. lt_upload TYPE zif_abapgit_definitions=>ty_git_branch_list_tt.
lo_branch_list = zcl_abapgit_git_transport=>branches( ms_merge-repo->get_url( ) ).
ms_merge-source = lo_branch_list->find_by_name(
zcl_abapgit_git_branch_list=>complete_heads_branch_name( mv_source_branch ) ).
ms_merge-target = lo_branch_list->find_by_name(
zcl_abapgit_git_branch_list=>complete_heads_branch_name( mv_target_branch ) ).
lo_branch_list = zcl_abapgit_git_transport=>branches( gs_merge-repo->get_url( ) ). APPEND ms_merge-source TO lt_upload.
gs_merge-source = lo_branch_list->find_by_name( APPEND ms_merge-target TO lt_upload.
zcl_abapgit_git_branch_list=>complete_heads_branch_name( iv_source ) ).
gs_merge-target = lo_branch_list->find_by_name(
zcl_abapgit_git_branch_list=>complete_heads_branch_name( iv_target ) ).
APPEND gs_merge-source TO lt_upload.
APPEND gs_merge-target TO lt_upload.
zcl_abapgit_git_transport=>upload_pack( zcl_abapgit_git_transport=>upload_pack(
EXPORTING EXPORTING
iv_url = gs_merge-repo->get_url( ) iv_url = ms_merge-repo->get_url( )
iv_branch_name = gs_merge-repo->get_branch_name( ) iv_branch_name = ms_merge-repo->get_branch_name( )
iv_deepen = abap_false iv_deepen = abap_false
it_branches = lt_upload it_branches = lt_upload
IMPORTING IMPORTING
et_objects = gt_objects ). et_objects = rt_objects ).
ENDMETHOD. ENDMETHOD.
@ -212,13 +292,13 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
lv_commit LIKE LINE OF lt_visit. lv_commit LIKE LINE OF lt_visit.
FIELD-SYMBOLS: <ls_ancestor> LIKE LINE OF rt_ancestors, FIELD-SYMBOLS: <ls_ancestor> LIKE LINE OF rt_ancestors,
<ls_object> LIKE LINE OF gt_objects. <ls_object> LIKE LINE OF mt_objects.
APPEND iv_commit TO lt_visit. APPEND iv_commit TO lt_visit.
LOOP AT lt_visit INTO lv_commit. LOOP AT lt_visit INTO lv_commit.
READ TABLE gt_objects ASSIGNING <ls_object> READ TABLE mt_objects ASSIGNING <ls_object>
WITH KEY type = zif_abapgit_definitions=>gc_type-commit sha1 = lv_commit. WITH KEY type = zif_abapgit_definitions=>gc_type-commit sha1 = lv_commit.
ASSERT sy-subrc = 0. ASSERT sy-subrc = 0.
@ -231,9 +311,11 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
<ls_ancestor>-commit = lv_commit. <ls_ancestor>-commit = lv_commit.
<ls_ancestor>-tree = ls_commit-tree. <ls_ancestor>-tree = ls_commit-tree.
<ls_ancestor>-body = ls_commit-body. <ls_ancestor>-body = ls_commit-body.
FIND REGEX zif_abapgit_definitions=>gc_author_regex IN ls_commit-author <ls_ancestor>-time = ls_commit-author.
SUBMATCHES <ls_ancestor>-time ##NO_TEXT.
ASSERT sy-subrc = 0. "Strip Author entry of all but the time component
REPLACE ALL OCCURRENCES OF REGEX '[a-zA-Z<>@.-]*' IN <ls_ancestor>-time WITH ''.
CONDENSE <ls_ancestor>-time.
ENDLOOP. ENDLOOP.
SORT rt_ancestors BY time DESCENDING. SORT rt_ancestors BY time DESCENDING.
@ -246,7 +328,6 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
FIELD-SYMBOLS: <ls_list1> LIKE LINE OF it_list1, FIELD-SYMBOLS: <ls_list1> LIKE LINE OF it_list1,
<ls_list2> LIKE LINE OF it_list2. <ls_list2> LIKE LINE OF it_list2.
LOOP AT it_list1 ASSIGNING <ls_list1>. LOOP AT it_list1 ASSIGNING <ls_list1>.
LOOP AT it_list2 ASSIGNING <ls_list2>. LOOP AT it_list2 ASSIGNING <ls_list2>.
IF <ls_list1>-tree = <ls_list2>-tree. IF <ls_list1>-tree = <ls_list2>-tree.
@ -261,42 +342,107 @@ CLASS ZCL_ABAPGIT_MERGE IMPLEMENTATION.
ENDMETHOD. ENDMETHOD.
METHOD get_conflicts.
rt_conflicts = mt_conflicts.
ENDMETHOD.
METHOD get_result.
rs_merge = ms_merge.
ENDMETHOD.
METHOD get_source_branch.
rv_source_branch = mv_source_branch.
ENDMETHOD.
METHOD get_target_branch.
rv_target_branch = mv_target_branch.
ENDMETHOD.
METHOD has_conflicts.
IF lines( mt_conflicts ) > 0.
rv_conflicts_exists = abap_true.
ELSE.
rv_conflicts_exists = abap_false.
ENDIF.
ENDMETHOD.
METHOD resolve_conflict.
FIELD-SYMBOLS: <ls_conflict> TYPE zif_abapgit_definitions=>ty_merge_conflict,
<ls_result> LIKE LINE OF ms_merge-result.
IF is_conflict-result_sha1 IS NOT INITIAL
AND is_conflict-result_data IS NOT INITIAL.
READ TABLE mt_conflicts ASSIGNING <ls_conflict> WITH KEY path = is_conflict-path
filename = is_conflict-filename.
IF sy-subrc EQ 0.
READ TABLE ms_merge-result ASSIGNING <ls_result> WITH KEY path = is_conflict-path
name = is_conflict-filename.
IF sy-subrc EQ 0.
<ls_result>-sha1 = is_conflict-result_sha1.
ms_merge-stage->add( iv_path = <ls_conflict>-path
iv_filename = <ls_conflict>-filename
iv_data = is_conflict-result_data ).
DELETE mt_conflicts WHERE path EQ is_conflict-path
AND filename EQ is_conflict-filename.
ENDIF.
READ TABLE ms_merge-result ASSIGNING <ls_result> WITH KEY sha1 = space.
IF sy-subrc EQ 0.
ms_merge-conflict = |{ <ls_result>-name } merge conflict, changed in source and target branch|.
ELSE.
CLEAR ms_merge-conflict.
ENDIF.
ENDIF.
ENDIF.
ENDMETHOD.
METHOD run. METHOD run.
DATA: lt_asource TYPE ty_ancestor_tt, DATA: lt_asource TYPE ty_ancestor_tt,
lt_atarget TYPE ty_ancestor_tt. lt_atarget TYPE ty_ancestor_tt.
CLEAR: ms_merge, mt_objects, mt_conflicts.
IF iv_source = iv_target. ms_merge-repo = mo_repo.
zcx_abapgit_exception=>raise( 'source = target' ). mt_objects = fetch_git( ).
ENDIF.
CLEAR gs_merge. lt_asource = find_ancestors( ms_merge-source-sha1 ).
lt_atarget = find_ancestors( ms_merge-target-sha1 ).
gs_merge-repo = io_repo. ms_merge-common = find_first_common( it_list1 = lt_asource
fetch_git( iv_source = iv_source
iv_target = iv_target ).
lt_asource = find_ancestors( gs_merge-source-sha1 ).
lt_atarget = find_ancestors( gs_merge-target-sha1 ).
gs_merge-common = find_first_common( it_list1 = lt_asource
it_list2 = lt_atarget ). it_list2 = lt_atarget ).
gs_merge-stree = zcl_abapgit_git_porcelain=>full_tree( ms_merge-stree = zcl_abapgit_git_porcelain=>full_tree(
it_objects = gt_objects it_objects = mt_objects
iv_branch = gs_merge-source-sha1 ). iv_branch = ms_merge-source-sha1 ).
gs_merge-ttree = zcl_abapgit_git_porcelain=>full_tree( ms_merge-ttree = zcl_abapgit_git_porcelain=>full_tree(
it_objects = gt_objects it_objects = mt_objects
iv_branch = gs_merge-target-sha1 ). iv_branch = ms_merge-target-sha1 ).
gs_merge-ctree = zcl_abapgit_git_porcelain=>full_tree( ms_merge-ctree = zcl_abapgit_git_porcelain=>full_tree(
it_objects = gt_objects it_objects = mt_objects
iv_branch = gs_merge-common-commit ). iv_branch = ms_merge-common-commit ).
calculate_result( ). calculate_result( ).
rs_merge = gs_merge.
ENDMETHOD. ENDMETHOD.
ENDCLASS. ENDCLASS.

View File

@ -14,6 +14,20 @@
<FIXPT>X</FIXPT> <FIXPT>X</FIXPT>
<UNICODE>X</UNICODE> <UNICODE>X</UNICODE>
</VSEOCLASS> </VSEOCLASS>
<DESCRIPTIONS>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_MERGE</CLSNAME>
<CMPNAME>CONSTRUCTOR</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
</SEOCOMPOTX>
<SEOCOMPOTX>
<CLSNAME>ZCL_ABAPGIT_MERGE</CLSNAME>
<CMPNAME>MO_REPO</CMPNAME>
<LANGU>E</LANGU>
<DESCRIPT>Online Repository</DESCRIPT>
</SEOCOMPOTX>
</DESCRIPTIONS>
</asx:values> </asx:values>
</asx:abap> </asx:abap>
</abapGit> </abapGit>

View File

@ -272,6 +272,18 @@ INTERFACE zif_abapgit_definitions PUBLIC.
conflict TYPE string, conflict TYPE string,
END OF ty_merge. END OF ty_merge.
TYPES: BEGIN OF ty_merge_conflict,
path TYPE string,
filename TYPE string,
source_sha1 TYPE zif_abapgit_definitions=>ty_sha1,
source_data TYPE xstring,
target_sha1 TYPE zif_abapgit_definitions=>ty_sha1,
target_data TYPE xstring,
result_sha1 TYPE zif_abapgit_definitions=>ty_sha1,
result_data TYPE xstring,
END OF ty_merge_conflict,
tt_merge_conflict TYPE STANDARD TABLE OF ty_merge_conflict WITH DEFAULT KEY.
TYPES: BEGIN OF ty_repo_item, TYPES: BEGIN OF ty_repo_item,
obj_type TYPE tadir-object, obj_type TYPE tadir-object,
obj_name TYPE tadir-obj_name, obj_name TYPE tadir-obj_name,