Author: danielsh Date: Sun Jul 26 11:59:58 2015 New Revision: 1692718 URL: http://svn.apache.org/r1692718 Log: On the patch-exec branch: patch: Parse mode changes out of git patches, part 1: low-level parser functionality.
* subversion/include/svn_diff.h (svn_patch_t.old_executable_p, svn_patch_t.new_executable_p): New members. * subversion/libsvn_diff/parse-diff.c (parse_state): New enum value 'state_old_mode_seen'. (parse_bits_into_executability): New helper function. (git_new_file, git_deleted_file): Parse the file's mode. (git_old_mode, git_new_mode): New state machine callback functions. (transitions): Hook the new callbacks. (svn_diff_parse_next_patch): Initialize new svn_patch_t members. * subversion/tests/libsvn_diff/parse-diff-test.c (git_unidiff): Add test data for "old mode" / "new mode" parsing. (git_tree_and_text_unidiff): Diversify existing test data. (test_parse_git_diff, test_parse_git_tree_and_text_diff): Test the new parsing. Modified: subversion/branches/patch-exec/subversion/include/svn_diff.h subversion/branches/patch-exec/subversion/libsvn_diff/parse-diff.c subversion/branches/patch-exec/subversion/tests/libsvn_diff/parse-diff-test.c Modified: subversion/branches/patch-exec/subversion/include/svn_diff.h URL: http://svn.apache.org/viewvc/subversion/branches/patch-exec/subversion/include/svn_diff.h?rev=1692718&r1=1692717&r2=1692718&view=diff ============================================================================== --- subversion/branches/patch-exec/subversion/include/svn_diff.h (original) +++ subversion/branches/patch-exec/subversion/include/svn_diff.h Sun Jul 26 11:59:58 2015 @@ -1249,6 +1249,17 @@ typedef struct svn_patch_t { * @since New in 1.9. */ svn_mergeinfo_t mergeinfo; svn_mergeinfo_t reverse_mergeinfo; + + /** The old and new executability bits, as retrieved from the patch file. + * + * #svn_tristate_unknown indicates the patch does not specify the + * corresponding bit. + */ + /* ### This is currently not parsed out of "index" lines (where it + * ### serves as an assertion of the executability state, without + * ### changing it). */ + svn_tristate_t old_executable_p; + svn_tristate_t new_executable_p; } svn_patch_t; /** An opaque type representing an open patch file. Modified: subversion/branches/patch-exec/subversion/libsvn_diff/parse-diff.c URL: http://svn.apache.org/viewvc/subversion/branches/patch-exec/subversion/libsvn_diff/parse-diff.c?rev=1692718&r1=1692717&r2=1692718&view=diff ============================================================================== --- subversion/branches/patch-exec/subversion/libsvn_diff/parse-diff.c (original) +++ subversion/branches/patch-exec/subversion/libsvn_diff/parse-diff.c Sun Jul 26 11:59:58 2015 @@ -922,6 +922,7 @@ enum parse_state state_git_tree_seen, /* a tree operation, rather then content change */ state_git_minus_seen, /* --- /dev/null; or --- a/ */ state_git_plus_seen, /* +++ /dev/null; or +++ a/ */ + state_old_mode_seen, /* old mode 100644 */ state_move_from_seen, /* rename from foo.c */ state_copy_from_seen, /* copy from foo.c */ state_minus_seen, /* --- foo.c */ @@ -1153,6 +1154,72 @@ git_plus(enum parse_state *new_state, ch return SVN_NO_ERROR; } +/* Helper for git_old_mode() and git_new_mode(). Translate the git + * file mode MODE_STR into a binary "executable?" notion EXECUTABLE_P. */ +static svn_error_t * +parse_bits_into_executability(svn_tristate_t *executable_p, + const char *mode_str) +{ + apr_uint64_t mode; + SVN_ERR(svn_cstring_strtoui64(&mode, mode_str, + 0 /* min */, + 0777777 /* max: six octal digits */, + 010 /* radix (octal) */)); + switch (mode & 0777) + { + case 0644: + *executable_p = svn_tristate_false; + break; + + case 0755: + *executable_p = svn_tristate_true; + break; + + default: + /* Ignore unknown values. */ + *executable_p = svn_tristate_unknown; + break; + } + + return SVN_NO_ERROR; +} + +/* Parse the 'old mode ' line of a git extended unidiff. */ +static svn_error_t * +git_old_mode(enum parse_state *new_state, char *line, svn_patch_t *patch, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + SVN_ERR(parse_bits_into_executability(&patch->old_executable_p, + line + STRLEN_LITERAL("old mode "))); + +#ifdef SVN_DEBUG + /* If this assert trips, the "old mode" is neither ...644 nor ...755 . */ + SVN_ERR_ASSERT(patch->old_executable_p != svn_tristate_unknown); +#endif + + *new_state = state_old_mode_seen; + return SVN_NO_ERROR; +} + +/* Parse the 'new mode ' line of a git extended unidiff. */ +static svn_error_t * +git_new_mode(enum parse_state *new_state, char *line, svn_patch_t *patch, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + SVN_ERR(parse_bits_into_executability(&patch->new_executable_p, + line + STRLEN_LITERAL("new mode "))); + +#ifdef SVN_DEBUG + /* If this assert trips, the "old mode" is neither ...644 nor ...755 . */ + SVN_ERR_ASSERT(patch->new_executable_p != svn_tristate_unknown); +#endif + + /* Don't touch patch->operation. */ + + *new_state = state_git_tree_seen; + return SVN_NO_ERROR; +} + /* Parse the 'rename from ' line of a git extended unidiff. */ static svn_error_t * git_move_from(enum parse_state *new_state, char *line, svn_patch_t *patch, @@ -1213,6 +1280,10 @@ static svn_error_t * git_new_file(enum parse_state *new_state, char *line, svn_patch_t *patch, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + SVN_ERR( + parse_bits_into_executability(&patch->new_executable_p, + line + STRLEN_LITERAL("new file mode "))); + patch->operation = svn_diff_op_added; /* Filename already retrieved from diff --git header. */ @@ -1226,6 +1297,10 @@ static svn_error_t * git_deleted_file(enum parse_state *new_state, char *line, svn_patch_t *patch, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + SVN_ERR( + parse_bits_into_executability(&patch->old_executable_p, + line + STRLEN_LITERAL("deleted file mode "))); + patch->operation = svn_diff_op_deleted; /* Filename already retrieved from diff --git header. */ @@ -1360,6 +1435,9 @@ static struct transition transitions[] = {"+++ b/", state_git_minus_seen, git_plus}, {"+++ /dev/null", state_git_minus_seen, git_plus}, + {"old mode ", state_git_diff_seen, git_old_mode}, + {"new mode ", state_old_mode_seen, git_new_mode}, + {"rename from ", state_git_diff_seen, git_move_from}, {"rename to ", state_move_from_seen, git_move_to}, @@ -1394,6 +1472,8 @@ svn_diff_parse_next_patch(svn_patch_t ** } patch = apr_pcalloc(result_pool, sizeof(*patch)); + patch->old_executable_p = svn_tristate_unknown; + patch->new_executable_p = svn_tristate_unknown; pos = patch_file->next_patch_offset; SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &pos, scratch_pool)); Modified: subversion/branches/patch-exec/subversion/tests/libsvn_diff/parse-diff-test.c URL: http://svn.apache.org/viewvc/subversion/branches/patch-exec/subversion/tests/libsvn_diff/parse-diff-test.c?rev=1692718&r1=1692717&r2=1692718&view=diff ============================================================================== --- subversion/branches/patch-exec/subversion/tests/libsvn_diff/parse-diff-test.c (original) +++ subversion/branches/patch-exec/subversion/tests/libsvn_diff/parse-diff-test.c Sun Jul 26 11:59:58 2015 @@ -66,6 +66,8 @@ static const char *git_unidiff = "Index: A/C/gamma" NL "===================================================================" NL "diff --git a/A/C/gamma b/A/C/gamma" NL + "old mode 100644" NL + "new mode 100755" NL "--- a/A/C/gamma\t(revision 2)" NL "+++ b/A/C/gamma\t(working copy)" NL "@@ -1 +1,2 @@" NL @@ -114,7 +116,7 @@ static const char *git_tree_and_text_uni "Index: A/B/lambda" NL "===================================================================" NL "diff --git a/A/B/lambda b/A/B/lambda" NL - "deleted file mode 100644" NL + "deleted file mode 100755" NL "--- a/A/B/lambda\t(revision 2)" NL "+++ /dev/null\t(working copy)" NL "@@ -1 +0,0 @@" NL @@ -447,6 +449,8 @@ test_parse_git_diff(apr_pool_t *pool) SVN_TEST_STRING_ASSERT(patch->new_filename, "A/C/gamma"); SVN_TEST_ASSERT(patch->operation == svn_diff_op_modified); SVN_TEST_ASSERT(patch->hunks->nelts == 1); + SVN_TEST_ASSERT(patch->old_executable_p = svn_tristate_false); + SVN_TEST_ASSERT(patch->new_executable_p = svn_tristate_true); hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *); @@ -482,6 +486,8 @@ test_parse_git_diff(apr_pool_t *pool) SVN_TEST_STRING_ASSERT(patch->new_filename, "new"); SVN_TEST_ASSERT(patch->operation == svn_diff_op_added); SVN_TEST_ASSERT(patch->hunks->nelts == 0); + SVN_TEST_ASSERT(patch->old_executable_p = svn_tristate_unknown); + SVN_TEST_ASSERT(patch->new_executable_p = svn_tristate_false); SVN_ERR(svn_diff_close_patch_file(patch_file, pool)); @@ -572,6 +578,8 @@ test_parse_git_tree_and_text_diff(apr_po SVN_TEST_STRING_ASSERT(patch->new_filename, "/dev/null"); SVN_TEST_ASSERT(patch->operation == svn_diff_op_deleted); SVN_TEST_ASSERT(patch->hunks->nelts == 1); + SVN_TEST_ASSERT(patch->old_executable_p = svn_tristate_true); + SVN_TEST_ASSERT(patch->new_executable_p = svn_tristate_unknown); hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);