CLASS zcl_abapgit_git_pack DEFINITION PUBLIC CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF ty_node, chmod TYPE zif_abapgit_definitions=>ty_chmod, name TYPE string, sha1 TYPE zif_abapgit_definitions=>ty_sha1, END OF ty_node . TYPES: ty_nodes_tt TYPE STANDARD TABLE OF ty_node WITH DEFAULT KEY . TYPES: BEGIN OF ty_commit, tree TYPE zif_abapgit_definitions=>ty_sha1, parent TYPE zif_abapgit_definitions=>ty_sha1, parent2 TYPE zif_abapgit_definitions=>ty_sha1, author TYPE string, committer TYPE string, body TYPE string, END OF ty_commit . TYPES: BEGIN OF ty_tag, object TYPE string, type TYPE string, tag TYPE string, tagger_name TYPE string, tagger_email TYPE string, message TYPE string, body TYPE string, END OF ty_tag . CLASS-METHODS decode IMPORTING !iv_data TYPE xstring RETURNING VALUE(rt_objects) TYPE zif_abapgit_definitions=>ty_objects_tt RAISING zcx_abapgit_exception . CLASS-METHODS decode_tree IMPORTING !iv_data TYPE xstring RETURNING VALUE(rt_nodes) TYPE ty_nodes_tt RAISING zcx_abapgit_exception . CLASS-METHODS decode_commit IMPORTING !iv_data TYPE xstring RETURNING VALUE(rs_commit) TYPE ty_commit RAISING zcx_abapgit_exception . CLASS-METHODS decode_tag IMPORTING !iv_data TYPE xstring RETURNING VALUE(rs_tag) TYPE ty_tag RAISING zcx_abapgit_exception . CLASS-METHODS encode IMPORTING !it_objects TYPE zif_abapgit_definitions=>ty_objects_tt RETURNING VALUE(rv_data) TYPE xstring RAISING zcx_abapgit_exception . CLASS-METHODS encode_tree IMPORTING !it_nodes TYPE ty_nodes_tt RETURNING VALUE(rv_data) TYPE xstring . CLASS-METHODS encode_commit IMPORTING !is_commit TYPE ty_commit RETURNING VALUE(rv_data) TYPE xstring . CLASS-METHODS encode_tag IMPORTING !is_tag TYPE zcl_abapgit_git_pack=>ty_tag RETURNING VALUE(rv_data) TYPE xstring RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_pack_start TYPE x LENGTH 4 VALUE '5041434B' ##NO_TEXT. CONSTANTS: c_zlib TYPE x LENGTH 2 VALUE '789C' ##NO_TEXT. CONSTANTS: c_zlib_hmm TYPE x LENGTH 2 VALUE '7801' ##NO_TEXT. CONSTANTS: " PACK c_version TYPE x LENGTH 4 VALUE '00000002' ##NO_TEXT. CLASS-METHODS decode_deltas CHANGING !ct_objects TYPE zif_abapgit_definitions=>ty_objects_tt RAISING zcx_abapgit_exception . CLASS-METHODS delta IMPORTING !is_object TYPE zif_abapgit_definitions=>ty_object CHANGING !ct_objects TYPE zif_abapgit_definitions=>ty_objects_tt RAISING zcx_abapgit_exception . CLASS-METHODS delta_header EXPORTING !ev_header TYPE i CHANGING !cv_delta TYPE xstring . CLASS-METHODS sort_tree IMPORTING !it_nodes TYPE ty_nodes_tt RETURNING VALUE(rt_nodes) TYPE ty_nodes_tt . CLASS-METHODS get_type IMPORTING !iv_x TYPE x RETURNING VALUE(rv_type) TYPE zif_abapgit_definitions=>ty_type RAISING zcx_abapgit_exception . CLASS-METHODS get_length EXPORTING !ev_length TYPE i CHANGING !cv_data TYPE xstring . CLASS-METHODS type_and_length IMPORTING !iv_type TYPE zif_abapgit_definitions=>ty_type !iv_length TYPE i RETURNING VALUE(rv_xstring) TYPE xstring RAISING zcx_abapgit_exception . CLASS-METHODS zlib_decompress CHANGING !cv_data TYPE xstring !cv_decompressed TYPE xstring RAISING zcx_abapgit_exception . ENDCLASS. CLASS ZCL_ABAPGIT_GIT_PACK IMPLEMENTATION. METHOD decode. DATA: lv_x TYPE x, lv_data TYPE xstring, lv_type TYPE c LENGTH 6, lv_zlib TYPE x LENGTH 2, lv_objects TYPE i, lv_len TYPE i, lv_sha1 TYPE zif_abapgit_definitions=>ty_sha1, lv_ref_delta TYPE zif_abapgit_definitions=>ty_sha1, lv_compressed_len TYPE i, lv_compressed TYPE xstring, lv_decompressed TYPE xstring, lv_decompress_len TYPE i, lv_xstring TYPE xstring, lv_expected TYPE i, ls_object LIKE LINE OF rt_objects, lv_uindex TYPE sy-index. lv_data = iv_data. * header IF NOT xstrlen( lv_data ) > 4 OR lv_data(4) <> c_pack_start. zcx_abapgit_exception=>raise( |Unexpected pack header| ). ENDIF. lv_data = lv_data+4. * version IF lv_data(4) <> c_version. zcx_abapgit_exception=>raise( |Version not supported| ). ENDIF. lv_data = lv_data+4. * number of objects lv_xstring = lv_data(4). lv_objects = zcl_abapgit_convert=>xstring_to_int( lv_xstring ). lv_data = lv_data+4. DO lv_objects TIMES. lv_uindex = sy-index. lv_x = lv_data(1). lv_type = get_type( lv_x ). get_length( IMPORTING ev_length = lv_expected CHANGING cv_data = lv_data ). IF lv_type = zif_abapgit_definitions=>c_type-ref_d. lv_ref_delta = lv_data(20). lv_data = lv_data+20. ENDIF. * strip header, '789C', CMF + FLG lv_zlib = lv_data(2). IF lv_zlib <> c_zlib AND lv_zlib <> c_zlib_hmm. zcx_abapgit_exception=>raise( |Unexpected zlib header| ). ENDIF. lv_data = lv_data+2. ******************************* IF lv_zlib = c_zlib. cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = lv_data IMPORTING raw_out = lv_decompressed raw_out_len = lv_decompress_len ). IF lv_expected <> lv_decompress_len. zcx_abapgit_exception=>raise( |Decompression falied| ). ENDIF. cl_abap_gzip=>compress_binary( EXPORTING raw_in = lv_decompressed IMPORTING gzip_out = lv_compressed gzip_out_len = lv_compressed_len ). IF lv_compressed(lv_compressed_len) <> lv_data(lv_compressed_len). "Lets try with zlib before error in out for good "This fixes issues with TFS 2017 and visualstudio.com Git repos zlib_decompress( CHANGING cv_data = lv_data cv_decompressed = lv_decompressed ). ELSE. lv_data = lv_data+lv_compressed_len. ENDIF. ELSEIF lv_zlib = c_zlib_hmm. * cl_abap_gzip copmression works for header '789C', but does not work for * '7801', call custom implementation of DEFLATE algorithm. * The custom implementation could handle both, but most likely the kernel * implementation runs faster than the custom ABAP. zlib_decompress( CHANGING cv_data = lv_data cv_decompressed = lv_decompressed ). ENDIF. CLEAR ls_object. ls_object-adler32 = lv_data(4). lv_data = lv_data+4. " skip adler checksum IF lv_type = zif_abapgit_definitions=>c_type-ref_d. ls_object-sha1 = lv_ref_delta. TRANSLATE ls_object-sha1 TO LOWER CASE. ELSE. ls_object-sha1 = zcl_abapgit_hash=>sha1( iv_type = lv_type iv_data = lv_decompressed ). ENDIF. ls_object-type = lv_type. ls_object-data = lv_decompressed. ls_object-index = lv_uindex. APPEND ls_object TO rt_objects. ENDDO. * check SHA1 at end of pack lv_len = xstrlen( iv_data ) - 20. lv_xstring = iv_data(lv_len). lv_sha1 = zcl_abapgit_hash=>sha1_raw( lv_xstring ). IF to_upper( lv_sha1 ) <> lv_data. zcx_abapgit_exception=>raise( |SHA1 at end of pack doesnt match| ). ENDIF. decode_deltas( CHANGING ct_objects = rt_objects ). ENDMETHOD. METHOD decode_commit. DATA: lv_string TYPE string, lv_word TYPE string, lv_length TYPE i, lv_trash TYPE string ##NEEDED, lt_string TYPE TABLE OF string. FIELD-SYMBOLS: LIKE LINE OF lt_string. lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( iv_data ). SPLIT lv_string AT zif_abapgit_definitions=>c_newline INTO TABLE lt_string. LOOP AT lt_string ASSIGNING . lv_length = strlen( ) + 1. lv_string = lv_string+lv_length. SPLIT AT space INTO lv_word lv_trash. CASE lv_word. WHEN 'tree'. rs_commit-tree = +5. WHEN 'parent'. IF rs_commit-parent IS INITIAL. rs_commit-parent = +7. ELSE. rs_commit-parent2 = +7. ENDIF. WHEN 'author'. rs_commit-author = +7. WHEN 'committer'. rs_commit-committer = +10. EXIT. " current loop WHEN OTHERS. ASSERT 0 = 1. ENDCASE. ENDLOOP. rs_commit-body = lv_string+1. IF rs_commit-author IS INITIAL OR rs_commit-committer IS INITIAL OR rs_commit-tree IS INITIAL. zcx_abapgit_exception=>raise( |multiple parents? not supported| ). ENDIF. ENDMETHOD. METHOD decode_deltas. DATA: ls_object LIKE LINE OF ct_objects, li_progress TYPE REF TO zif_abapgit_progress, lt_deltas LIKE ct_objects. LOOP AT ct_objects INTO ls_object USING KEY type WHERE type = zif_abapgit_definitions=>c_type-ref_d. INSERT ls_object INTO TABLE lt_deltas. ENDLOOP. DELETE ct_objects USING KEY type WHERE type = zif_abapgit_definitions=>c_type-ref_d. "Restore correct Delta Order SORT lt_deltas BY index. li_progress = zcl_abapgit_progress=>get_instance( lines( lt_deltas ) ). LOOP AT lt_deltas INTO ls_object. li_progress->show( iv_current = sy-tabix iv_text = 'Decode deltas' ) ##NO_TEXT. delta( EXPORTING is_object = ls_object CHANGING ct_objects = ct_objects ). ENDLOOP. ENDMETHOD. METHOD decode_tag. DATA: lv_string TYPE string, lv_word TYPE string, lv_trash TYPE string ##NEEDED, lt_string TYPE TABLE OF string. FIELD-SYMBOLS: LIKE LINE OF lt_string. lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( iv_data ). SPLIT lv_string AT zif_abapgit_definitions=>c_newline INTO TABLE lt_string. LOOP AT lt_string ASSIGNING . SPLIT AT space INTO lv_word lv_trash. CASE lv_word. WHEN 'object'. rs_tag-object = lv_trash. WHEN 'type'. rs_tag-type = lv_trash. WHEN 'tag'. rs_tag-tag = lv_trash. WHEN 'tagger'. FIND FIRST OCCURRENCE OF REGEX `(.*)<(.*)>` IN lv_trash SUBMATCHES rs_tag-tagger_name rs_tag-tagger_email. rs_tag-tagger_name = condense( rs_tag-tagger_name ). WHEN ''. " ignore blank lines CONTINUE. WHEN OTHERS. " these are the non empty line which don't start with a key word " the first one is the message, the rest are cumulated to the body IF rs_tag-message IS INITIAL. rs_tag-message = . ELSE. IF rs_tag-body IS NOT INITIAL. rs_tag-body = rs_tag-body && zif_abapgit_definitions=>c_newline. ENDIF. rs_tag-body = rs_tag-body && . ENDIF. ENDCASE. ENDLOOP. ENDMETHOD. METHOD decode_tree. CONSTANTS: lc_sha_length TYPE i VALUE 20, lc_null TYPE x VALUE '00'. DATA: lv_xstring TYPE xstring, lv_chmod TYPE zif_abapgit_definitions=>ty_chmod, lv_name TYPE string, lv_string TYPE string, lv_len TYPE i, lv_offset TYPE i, lv_cursor TYPE i, lv_match TYPE i, ls_node TYPE ty_node. DO. FIND FIRST OCCURRENCE OF lc_null IN SECTION OFFSET lv_cursor OF iv_data IN BYTE MODE MATCH OFFSET lv_match. IF sy-subrc <> 0. EXIT. ENDIF. lv_len = lv_match - lv_cursor. lv_xstring = iv_data+lv_cursor(lv_len). lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xstring ). SPLIT lv_string AT space INTO lv_chmod lv_name. CLEAR ls_node. ls_node-chmod = lv_chmod. IF ls_node-chmod <> zif_abapgit_definitions=>c_chmod-dir AND ls_node-chmod <> zif_abapgit_definitions=>c_chmod-file AND ls_node-chmod <> zif_abapgit_definitions=>c_chmod-executable. zcx_abapgit_exception=>raise( |Unknown chmod| ). ENDIF. lv_offset = lv_match + 1. ls_node-name = lv_name. ls_node-sha1 = iv_data+lv_offset(lc_sha_length). TRANSLATE ls_node-sha1 TO LOWER CASE. APPEND ls_node TO rt_nodes. lv_cursor = lv_match + 1 + lc_sha_length. ENDDO. ENDMETHOD. METHOD delta. CONSTANTS: lc_1 TYPE x VALUE '01', lc_2 TYPE x VALUE '02', lc_4 TYPE x VALUE '04', lc_8 TYPE x VALUE '08', lc_16 TYPE x VALUE '10', lc_32 TYPE x VALUE '20', lc_64 TYPE x VALUE '40', lc_128 TYPE x VALUE '80'. DEFINE _eat_byte. lv_x = lv_delta(1). lv_delta = lv_delta+1. END-OF-DEFINITION. DATA: lv_delta TYPE xstring, lv_base TYPE xstring, lv_result TYPE xstring, lv_offset TYPE i, lv_sha1 TYPE zif_abapgit_definitions=>ty_sha1, ls_object LIKE LINE OF ct_objects, lv_len TYPE i, lv_org TYPE x, lv_x TYPE x. FIELD-SYMBOLS: LIKE LINE OF ct_objects. lv_delta = is_object-data. * find base READ TABLE ct_objects ASSIGNING WITH KEY sha COMPONENTS sha1 = is_object-sha1. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Base not found, { is_object-sha1 }| ). ELSEIF -type = zif_abapgit_definitions=>c_type-ref_d. * sanity check zcx_abapgit_exception=>raise( |Delta, base eq delta| ). ENDIF. lv_base = -data. * skip the 2 headers delta_header( CHANGING cv_delta = lv_delta ). delta_header( CHANGING cv_delta = lv_delta ). WHILE xstrlen( lv_delta ) > 0. _eat_byte. lv_org = lv_x. IF lv_x BIT-AND lc_128 = lc_128. " MSB = 1 lv_offset = 0. IF lv_org BIT-AND lc_1 = lc_1. _eat_byte. lv_offset = lv_x. ENDIF. IF lv_org BIT-AND lc_2 = lc_2. _eat_byte. lv_offset = lv_offset + lv_x * 256. ENDIF. IF lv_org BIT-AND lc_4 = lc_4. _eat_byte. lv_offset = lv_offset + lv_x * 65536. ENDIF. IF lv_org BIT-AND lc_8 = lc_8. _eat_byte. lv_offset = lv_offset + lv_x * 16777216. " hmm, overflow? ENDIF. lv_len = 0. IF lv_org BIT-AND lc_16 = lc_16. _eat_byte. lv_len = lv_x. ENDIF. IF lv_org BIT-AND lc_32 = lc_32. _eat_byte. lv_len = lv_len + lv_x * 256. ENDIF. IF lv_org BIT-AND lc_64 = lc_64. _eat_byte. lv_len = lv_len + lv_x * 65536. ENDIF. IF lv_len = 0. lv_len = 65536. ENDIF. CONCATENATE lv_result lv_base+lv_offset(lv_len) INTO lv_result IN BYTE MODE. ELSE. " lv_bitbyte(1) = '0' * insert from delta lv_len = lv_x. CONCATENATE lv_result lv_delta(lv_len) INTO lv_result IN BYTE MODE. lv_delta = lv_delta+lv_len. ENDIF. ENDWHILE. lv_sha1 = zcl_abapgit_hash=>sha1( iv_type = -type iv_data = lv_result ). CLEAR ls_object. ls_object-sha1 = lv_sha1. ls_object-type = -type. ls_object-data = lv_result. ls_object-index = -index. "Retain sort index APPEND ls_object TO ct_objects. ENDMETHOD. METHOD delta_header. DATA: lv_bitbyte TYPE zif_abapgit_definitions=>ty_bitbyte, lv_bits TYPE string, lv_x TYPE x. lv_bits = ''. DO. lv_x = cv_delta(1). cv_delta = cv_delta+1. lv_bitbyte = zcl_abapgit_convert=>x_to_bitbyte( lv_x ). CONCATENATE lv_bitbyte+1 lv_bits INTO lv_bits. IF lv_bitbyte(1) = '0'. EXIT. " current loop ENDIF. ENDDO. ev_header = zcl_abapgit_convert=>bitbyte_to_int( lv_bits ). ENDMETHOD. METHOD encode. DATA: lv_sha1 TYPE x LENGTH 20, lv_adler32 TYPE zif_abapgit_definitions=>ty_adler32, lv_compressed TYPE xstring, lv_xstring TYPE xstring, li_progress TYPE REF TO zif_abapgit_progress, lv_objects_total TYPE i. FIELD-SYMBOLS: LIKE LINE OF it_objects. rv_data = c_pack_start. CONCATENATE rv_data c_version INTO rv_data IN BYTE MODE. lv_xstring = zcl_abapgit_convert=>int_to_xstring4( lines( it_objects ) ). CONCATENATE rv_data lv_xstring INTO rv_data IN BYTE MODE. lv_objects_total = lines( it_objects ). li_progress = zcl_abapgit_progress=>get_instance( lv_objects_total ). LOOP AT it_objects ASSIGNING . IF sy-tabix MOD 200 = 0. li_progress->show( iv_current = sy-tabix iv_text = |Encoding objects ( { sy-tabix } of { lv_objects_total } )| ). ENDIF. lv_xstring = type_and_length( iv_type = -type iv_length = xstrlen( -data ) ). CONCATENATE rv_data lv_xstring INTO rv_data IN BYTE MODE. cl_abap_gzip=>compress_binary( EXPORTING raw_in = -data IMPORTING gzip_out = lv_compressed ). CONCATENATE rv_data c_zlib lv_compressed INTO rv_data IN BYTE MODE. IF NOT -adler32 IS INITIAL. lv_adler32 = -adler32. ELSE. lv_adler32 = zcl_abapgit_hash=>adler32( -data ). ENDIF. CONCATENATE rv_data lv_adler32 INTO rv_data IN BYTE MODE. ENDLOOP. lv_sha1 = to_upper( zcl_abapgit_hash=>sha1_raw( rv_data ) ). CONCATENATE rv_data lv_sha1 INTO rv_data IN BYTE MODE. ENDMETHOD. METHOD encode_commit. DATA: lv_string TYPE string, lv_tmp TYPE string, lv_tree_lower TYPE string, lv_parent_lower TYPE string. lv_tree_lower = is_commit-tree. TRANSLATE lv_tree_lower TO LOWER CASE. lv_string = ''. CONCATENATE 'tree' lv_tree_lower INTO lv_tmp SEPARATED BY space. "#EC NOTEXT CONCATENATE lv_string lv_tmp zif_abapgit_definitions=>c_newline INTO lv_string. IF NOT is_commit-parent IS INITIAL. lv_parent_lower = is_commit-parent. TRANSLATE lv_parent_lower TO LOWER CASE. CONCATENATE 'parent' lv_parent_lower INTO lv_tmp SEPARATED BY space. "#EC NOTEXT CONCATENATE lv_string lv_tmp zif_abapgit_definitions=>c_newline INTO lv_string. ENDIF. IF NOT is_commit-parent2 IS INITIAL. lv_parent_lower = is_commit-parent2. TRANSLATE lv_parent_lower TO LOWER CASE. CONCATENATE 'parent' lv_parent_lower INTO lv_tmp SEPARATED BY space. "#EC NOTEXT CONCATENATE lv_string lv_tmp zif_abapgit_definitions=>c_newline INTO lv_string. ENDIF. CONCATENATE 'author' is_commit-author INTO lv_tmp SEPARATED BY space. "#EC NOTEXT CONCATENATE lv_string lv_tmp zif_abapgit_definitions=>c_newline INTO lv_string. CONCATENATE 'committer' is_commit-committer INTO lv_tmp SEPARATED BY space. "#EC NOTEXT CONCATENATE lv_string lv_tmp zif_abapgit_definitions=>c_newline INTO lv_string. CONCATENATE lv_string zif_abapgit_definitions=>c_newline is_commit-body INTO lv_string. rv_data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ). ENDMETHOD. METHOD encode_tag. DATA: lv_string TYPE string, lv_time TYPE zcl_abapgit_time=>ty_unixtime. lv_time = zcl_abapgit_time=>get_unix( ). lv_string = |object { is_tag-object }{ zif_abapgit_definitions=>c_newline }| && |type { is_tag-type }{ zif_abapgit_definitions=>c_newline }| && |tag { zcl_abapgit_git_tag=>remove_tag_prefix( is_tag-tag ) }{ zif_abapgit_definitions=>c_newline }| && |tagger { is_tag-tagger_name } <{ is_tag-tagger_email }> { lv_time }| && |{ zif_abapgit_definitions=>c_newline }| && |{ zif_abapgit_definitions=>c_newline }| && |{ is_tag-message }|. rv_data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ). ENDMETHOD. METHOD encode_tree. CONSTANTS: lc_null TYPE x VALUE '00'. DATA: lv_string TYPE string, lt_nodes LIKE it_nodes, lv_hex20 TYPE x LENGTH 20, lv_xstring TYPE xstring. FIELD-SYMBOLS: LIKE LINE OF it_nodes. lt_nodes = sort_tree( it_nodes ). LOOP AT lt_nodes ASSIGNING . ASSERT NOT -chmod IS INITIAL. ASSERT NOT -name IS INITIAL. ASSERT NOT -sha1 IS INITIAL. CONCATENATE -chmod -name INTO lv_string SEPARATED BY space. lv_xstring = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ). lv_hex20 = to_upper( -sha1 ). CONCATENATE rv_data lv_xstring lc_null lv_hex20 INTO rv_data IN BYTE MODE. ENDLOOP. ENDMETHOD. METHOD get_length. DATA: lv_x TYPE x, lv_length_bits TYPE string, lv_bitbyte TYPE zif_abapgit_definitions=>ty_bitbyte. lv_x = cv_data(1). lv_bitbyte = zcl_abapgit_convert=>x_to_bitbyte( lv_x ). cv_data = cv_data+1. lv_length_bits = lv_bitbyte+4. WHILE lv_bitbyte(1) <> '0'. lv_x = cv_data(1). lv_bitbyte = zcl_abapgit_convert=>x_to_bitbyte( lv_x ). cv_data = cv_data+1. CONCATENATE lv_bitbyte+1 lv_length_bits INTO lv_length_bits. ENDWHILE. ev_length = zcl_abapgit_convert=>bitbyte_to_int( lv_length_bits ). ENDMETHOD. METHOD get_type. CONSTANTS: lc_mask TYPE x VALUE 112. DATA: lv_xtype TYPE x. lv_xtype = iv_x BIT-AND lc_mask. CASE lv_xtype. WHEN 16. rv_type = zif_abapgit_definitions=>c_type-commit. WHEN 32. rv_type = zif_abapgit_definitions=>c_type-tree. WHEN 48. rv_type = zif_abapgit_definitions=>c_type-blob. WHEN 64. rv_type = zif_abapgit_definitions=>c_type-tag. WHEN 112. rv_type = zif_abapgit_definitions=>c_type-ref_d. WHEN OTHERS. zcx_abapgit_exception=>raise( |Todo, unknown git pack type| ). ENDCASE. ENDMETHOD. METHOD sort_tree. TYPES: BEGIN OF ty_sort, sort TYPE string, node TYPE ty_node, END OF ty_sort. DATA: lt_sort TYPE STANDARD TABLE OF ty_sort WITH DEFAULT KEY. FIELD-SYMBOLS: LIKE LINE OF lt_sort, LIKE LINE OF it_nodes. LOOP AT it_nodes ASSIGNING . APPEND INITIAL LINE TO lt_sort ASSIGNING . IF -chmod = zif_abapgit_definitions=>c_chmod-dir. CONCATENATE -name '/' INTO -sort. ELSE. -sort = -name. ENDIF. -node = . ENDLOOP. * following has to be done, or unpack will fail on server side SORT lt_sort BY sort ASCENDING. LOOP AT lt_sort ASSIGNING . APPEND -node TO rt_nodes. ENDLOOP. ENDMETHOD. METHOD type_and_length. * see http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#pack_file_objects DATA: lv_type TYPE i, lv_length TYPE i, lv_hex TYPE x LENGTH 1. CASE iv_type. WHEN zif_abapgit_definitions=>c_type-commit. lv_type = 16. WHEN zif_abapgit_definitions=>c_type-tree. lv_type = 32. WHEN zif_abapgit_definitions=>c_type-blob. lv_type = 48. WHEN zif_abapgit_definitions=>c_type-tag. lv_type = 64. WHEN zif_abapgit_definitions=>c_type-ref_d. lv_type = 112. WHEN OTHERS. zcx_abapgit_exception=>raise( |Unexpected object type while encoding pack| ). ENDCASE. lv_length = iv_length. * first byte IF lv_length > 15. lv_hex = 128. ENDIF. lv_hex = lv_hex + lv_type + lv_length MOD 16. rv_xstring = lv_hex. lv_length = lv_length DIV 16. * subsequent bytes WHILE lv_length >= 128. lv_hex = 128 + lv_length MOD 128. CONCATENATE rv_xstring lv_hex INTO rv_xstring IN BYTE MODE. lv_length = lv_length DIV 128. ENDWHILE. * last byte IF lv_length > 0. lv_hex = lv_length. CONCATENATE rv_xstring lv_hex INTO rv_xstring IN BYTE MODE. ENDIF. ENDMETHOD. METHOD zlib_decompress. DATA: ls_data TYPE zcl_abapgit_zlib=>ty_decompress, lv_compressed_len TYPE i, lv_adler32 TYPE zif_abapgit_definitions=>ty_adler32. ls_data = zcl_abapgit_zlib=>decompress( cv_data ). lv_compressed_len = ls_data-compressed_len. cv_decompressed = ls_data-raw. IF lv_compressed_len IS INITIAL. zcx_abapgit_exception=>raise( |Decompression falied :o/| ). ENDIF. cv_data = cv_data+lv_compressed_len. lv_adler32 = zcl_abapgit_hash=>adler32( cv_decompressed ). IF cv_data(4) <> lv_adler32. cv_data = cv_data+1. ENDIF. IF cv_data(4) <> lv_adler32. cv_data = cv_data+1. ENDIF. IF cv_data(4) <> lv_adler32. zcx_abapgit_exception=>raise( |Wrong Adler checksum| ). ENDIF. ENDMETHOD. ENDCLASS.