diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_diff/parse-diff.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_diff/parse-diff.c')
-rw-r--r-- | subversion/libsvn_diff/parse-diff.c | 223 |
1 files changed, 181 insertions, 42 deletions
diff --git a/subversion/libsvn_diff/parse-diff.c b/subversion/libsvn_diff/parse-diff.c index e269ef9..3f794b8 100644 --- a/subversion/libsvn_diff/parse-diff.c +++ b/subversion/libsvn_diff/parse-diff.c @@ -35,9 +35,12 @@ #include "svn_utf.h" #include "svn_dirent_uri.h" #include "svn_diff.h" +#include "svn_ctype.h" +#include "svn_mergeinfo.h" #include "private/svn_eol_private.h" #include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" /* Helper macro for readability */ #define starts_with(str, start) \ @@ -385,7 +388,6 @@ svn_diff_hunk_readline_diff_text(svn_diff_hunk_t *hunk, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_diff_hunk_t dummy; svn_stringbuf_t *line; apr_size_t max_len; apr_off_t pos; @@ -415,33 +417,10 @@ svn_diff_hunk_readline_diff_text(svn_diff_hunk_t *hunk, if (hunk->patch->reverse) { - if (parse_hunk_header(line->data, &dummy, "@@", scratch_pool)) - { - /* Line is a hunk header, reverse it. */ - line = svn_stringbuf_createf(result_pool, - "@@ -%lu,%lu +%lu,%lu @@", - hunk->modified_start, - hunk->modified_length, - hunk->original_start, - hunk->original_length); - } - else if (parse_hunk_header(line->data, &dummy, "##", scratch_pool)) - { - /* Line is a hunk header, reverse it. */ - line = svn_stringbuf_createf(result_pool, - "## -%lu,%lu +%lu,%lu ##", - hunk->modified_start, - hunk->modified_length, - hunk->original_start, - hunk->original_length); - } - else - { - if (line->data[0] == '+') - line->data[0] = '-'; - else if (line->data[0] == '-') - line->data[0] = '+'; - } + if (line->data[0] == '+') + line->data[0] = '-'; + else if (line->data[0] == '-') + line->data[0] = '+'; } *stringbuf = line; @@ -471,6 +450,147 @@ parse_prop_name(const char **prop_name, const char *header, return SVN_NO_ERROR; } + +/* A helper function to parse svn:mergeinfo diffs. + * + * These diffs use a special pretty-print format, for instance: + * + * Added: svn:mergeinfo + * ## -0,0 +0,1 ## + * Merged /trunk:r2-3 + * + * The hunk header has the following format: + * ## -0,NUMBER_OF_REVERSE_MERGES +0,NUMBER_OF_FORWARD_MERGES ## + * + * At this point, the number of reverse merges has already been + * parsed into HUNK->ORIGINAL_LENGTH, and the number of forward + * merges has been parsed into HUNK->MODIFIED_LENGTH. + * + * The header is followed by a list of mergeinfo, one path per line. + * This function parses such lines. Lines describing reverse merges + * appear first, and then all lines describing forward merges appear. + * + * Parts of the line are affected by i18n. The words 'Merged' + * and 'Reverse-merged' can appear in any language and at any + * position within the line. We can only assume that a leading + * '/' starts the merge source path, the path is followed by + * ":r", which in turn is followed by a mergeinfo revision range, + * which is terminated by whitespace or end-of-string. + * + * If the current line meets the above criteria and we're able + * to parse valid mergeinfo from it, the resulting mergeinfo + * is added to patch->mergeinfo or patch->reverse_mergeinfo, + * and we proceed to the next line. + */ +static svn_error_t * +parse_mergeinfo(svn_boolean_t *found_mergeinfo, + svn_stringbuf_t *line, + svn_diff_hunk_t *hunk, + svn_patch_t *patch, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char *slash = strchr(line->data, '/'); + char *colon = strrchr(line->data, ':'); + + *found_mergeinfo = FALSE; + + if (slash && colon && colon[1] == 'r' && slash < colon) + { + svn_stringbuf_t *input; + svn_mergeinfo_t mergeinfo = NULL; + char *s; + svn_error_t *err; + + input = svn_stringbuf_create_ensure(line->len, scratch_pool); + + /* Copy the merge source path + colon */ + s = slash; + while (s <= colon) + { + svn_stringbuf_appendbyte(input, *s); + s++; + } + + /* skip 'r' after colon */ + s++; + + /* Copy the revision range. */ + while (s < line->data + line->len) + { + if (svn_ctype_isspace(*s)) + break; + svn_stringbuf_appendbyte(input, *s); + s++; + } + + err = svn_mergeinfo_parse(&mergeinfo, input->data, result_pool); + if (err && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) + { + svn_error_clear(err); + mergeinfo = NULL; + } + else + SVN_ERR(err); + + if (mergeinfo) + { + if (hunk->original_length > 0) /* reverse merges */ + { + if (patch->reverse) + { + if (patch->mergeinfo == NULL) + patch->mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + else + { + if (patch->reverse_mergeinfo == NULL) + patch->reverse_mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->reverse_mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + hunk->original_length--; + } + else if (hunk->modified_length > 0) /* forward merges */ + { + if (patch->reverse) + { + if (patch->reverse_mergeinfo == NULL) + patch->reverse_mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->reverse_mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + else + { + if (patch->mergeinfo == NULL) + patch->mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + hunk->modified_length--; + } + + *found_mergeinfo = TRUE; + } + } + + return SVN_NO_ERROR; +} + /* Return the next *HUNK from a PATCH in APR_FILE. * If no hunk can be found, set *HUNK to NULL. * Set IS_PROPERTY to TRUE if we have a property hunk. If the returned HUNK @@ -600,6 +720,17 @@ parse_next_hunk(svn_diff_hunk_t **hunk, continue; } + if (in_hunk && *is_property && *prop_name && + strcmp(*prop_name, SVN_PROP_MERGEINFO) == 0) + { + svn_boolean_t found_mergeinfo; + + SVN_ERR(parse_mergeinfo(&found_mergeinfo, line, *hunk, patch, + result_pool, iterpool)); + if (found_mergeinfo) + continue; /* Proceed to the next line in the patch. */ + } + if (in_hunk) { char c; @@ -1192,6 +1323,13 @@ parse_hunks(svn_patch_t *patch, apr_file_t *apr_file, prop_name = last_prop_name; else last_prop_name = prop_name; + + /* Skip svn:mergeinfo properties. + * Mergeinfo data cannot be represented as a hunk and + * is therefore stored in PATCH itself. */ + if (strcmp(prop_name, SVN_PROP_MERGEINFO) == 0) + continue; + SVN_ERR(add_property_hunk(patch, prop_name, hunk, prop_operation, result_pool)); } @@ -1229,7 +1367,7 @@ static struct transition transitions[] = }; svn_error_t * -svn_diff_parse_next_patch(svn_patch_t **patch, +svn_diff_parse_next_patch(svn_patch_t **patch_p, svn_patch_file_t *patch_file, svn_boolean_t reverse, svn_boolean_t ignore_whitespace, @@ -1240,16 +1378,17 @@ svn_diff_parse_next_patch(svn_patch_t **patch, svn_boolean_t eof; svn_boolean_t line_after_tree_header_read = FALSE; apr_pool_t *iterpool; + svn_patch_t *patch; enum parse_state state = state_start; if (apr_file_eof(patch_file->apr_file) == APR_EOF) { /* No more patches here. */ - *patch = NULL; + *patch_p = NULL; return SVN_NO_ERROR; } - *patch = apr_pcalloc(result_pool, sizeof(**patch)); + patch = apr_pcalloc(result_pool, sizeof(*patch)); pos = patch_file->next_patch_offset; SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &pos, scratch_pool)); @@ -1282,7 +1421,7 @@ svn_diff_parse_next_patch(svn_patch_t **patch, if (starts_with(line->data, transitions[i].expected_input) && state == transitions[i].required_state) { - SVN_ERR(transitions[i].fn(&state, line->data, *patch, + SVN_ERR(transitions[i].fn(&state, line->data, patch, result_pool, iterpool)); valid_header_line = TRUE; break; @@ -1328,22 +1467,22 @@ svn_diff_parse_next_patch(svn_patch_t **patch, } while (! eof); - (*patch)->reverse = reverse; + patch->reverse = reverse; if (reverse) { const char *temp; - temp = (*patch)->old_filename; - (*patch)->old_filename = (*patch)->new_filename; - (*patch)->new_filename = temp; + temp = patch->old_filename; + patch->old_filename = patch->new_filename; + patch->new_filename = temp; } - if ((*patch)->old_filename == NULL || (*patch)->new_filename == NULL) + if (patch->old_filename == NULL || patch->new_filename == NULL) { /* Something went wrong, just discard the result. */ - *patch = NULL; + patch = NULL; } else - SVN_ERR(parse_hunks(*patch, patch_file->apr_file, ignore_whitespace, + SVN_ERR(parse_hunks(patch, patch_file->apr_file, ignore_whitespace, result_pool, iterpool)); svn_pool_destroy(iterpool); @@ -1352,16 +1491,16 @@ svn_diff_parse_next_patch(svn_patch_t **patch, SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_CUR, &patch_file->next_patch_offset, scratch_pool)); - if (*patch) + if (patch) { /* Usually, hunks appear in the patch sorted by their original line * offset. But just in case they weren't parsed in this order for * some reason, we sort them so that our caller can assume that hunks * are sorted as if parsed from a usual patch. */ - qsort((*patch)->hunks->elts, (*patch)->hunks->nelts, - (*patch)->hunks->elt_size, compare_hunks); + svn_sort__array(patch->hunks, compare_hunks); } + *patch_p = patch; return SVN_NO_ERROR; } |