Index: subversion/include/svn_props.h =================================================================== --- subversion/include/svn_props.h (revision 1509957) +++ subversion/include/svn_props.h (working copy) @@ -412,6 +412,14 @@ svn_prop_name_is_valid(const char *prop_name); */ #define SVN_PROP_SPECIAL_VALUE SVN_PROP_BOOLEAN_TRUE +/** The value to force the executable property to when set. + * + * @deprecated Provided for backward compatibility with the 1.4 API. + * Use @c SVN_PROP_BOOLEAN_TRUE instead. + * @since New in 1.9. + */ +#define SVN_PROP_USE_COMMIT_TIMES_VALUE SVN_PROP_BOOLEAN_TRUE + /** Describes external items to check out into this directory. * * The format is a series of lines, each in the following format: @@ -449,6 +457,12 @@ svn_prop_name_is_valid(const char *prop_name); /** Property used to record inheritable configuration ignores. */ #define SVN_PROP_INHERITABLE_IGNORES SVN_PROP_PREFIX "global-ignores" +/** + * Property used to control whether timestamp of a file is modified to the commit time * @since New in 1.9. + * @since New in 1.9. + */ +#define SVN_PROP_USE_COMMIT_TIMES SVN_PROP_PREFIX "use-commit-times" + /** Meta-data properties. * * The following properties are used for storing meta-data about @@ -509,7 +523,8 @@ svn_prop_name_is_valid(const char *prop_name); SVN_PROP_TEXT_TIME, \ SVN_PROP_OWNER, \ SVN_PROP_GROUP, \ - SVN_PROP_UNIX_MODE, + SVN_PROP_UNIX_MODE, \ + SVN_PROP_USE_COMMIT_TIMES, /** @} */ Index: subversion/libsvn_subr/properties.c =================================================================== --- subversion/libsvn_subr/properties.c (revision 1509957) +++ subversion/libsvn_subr/properties.c (working copy) @@ -446,7 +446,8 @@ svn_prop_is_boolean(const char *prop_name) make any speed difference. */ if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0 || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0 - || strcmp(prop_name, SVN_PROP_SPECIAL) == 0) + || strcmp(prop_name, SVN_PROP_SPECIAL) == 0 + || strcmp(prop_name, SVN_PROP_USE_COMMIT_TIMES) == 0) return TRUE; return FALSE; } Index: subversion/libsvn_wc/props.c =================================================================== --- subversion/libsvn_wc/props.c (revision 1509957) +++ subversion/libsvn_wc/props.c (working copy) @@ -2152,7 +2152,7 @@ svn_wc_canonicalize_svn_prop(const svn_string_t ** } else if (svn_prop_is_boolean(propname)) { - /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL */ + /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL, SVN_PROP_USE_COMMIT_TIMES */ propval = &boolean_value; } else if (strcmp(propname, SVN_PROP_MERGEINFO) == 0) Index: subversion/libsvn_wc/update_editor.c =================================================================== --- subversion/libsvn_wc/update_editor.c (revision 1509957) +++ subversion/libsvn_wc/update_editor.c (working copy) @@ -751,6 +751,12 @@ struct file_baton /* The tree conflict to install once the node is really edited */ svn_skel_t *edit_conflict; + + /* use-commit-times */ + svn_boolean_t use_commit_times; + + /* remove use-commit-times */ + svn_boolean_t remove_use_commit_times; }; @@ -3830,6 +3836,17 @@ change_file_prop(void *file_baton, fb->already_notified = TRUE; } } + if (strcmp(name, SVN_PROP_USE_COMMIT_TIMES) == 0) + { + if (propchange->value) + { + fb->use_commit_times = TRUE; + } + else + { + fb->remove_use_commit_times = TRUE; + } + } return SVN_NO_ERROR; } @@ -4636,6 +4653,24 @@ close_file(void *file_baton, eb->notify_func(eb->notify_baton, notify, scratch_pool); } + /* use_commit_times and remove_use_commit_times are mutably exclusive */ + SVN_ERR_ASSERT( (fb->use_commit_times & fb->remove_use_commit_times) == 0 ); + if ( !fb->adding_file ) + { + if ( fb->use_commit_times && fb->changed_date ) + { + SVN_ERR(svn_io_set_file_affected_time(fb->changed_date, + fb->local_abspath, + scratch_pool)); + } + else if ( fb->remove_use_commit_times ) + { + SVN_ERR(svn_io_set_file_affected_time(apr_time_now(), + fb->local_abspath, + scratch_pool)); + } + } + svn_pool_destroy(fb->pool); /* Destroy scratch_pool */ /* We have one less referrer to the directory */ Index: subversion/libsvn_wc/workqueue.c =================================================================== --- subversion/libsvn_wc/workqueue.c (revision 1509957) +++ subversion/libsvn_wc/workqueue.c (working copy) @@ -638,6 +638,9 @@ run_file_install(work_item_baton_t *wqb, SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); } + if (props && svn_hash_gets(props, SVN_PROP_USE_COMMIT_TIMES)) + use_commit_times = TRUE; + if (use_commit_times) { if (changed_date) Index: subversion/svn/svn.c =================================================================== --- subversion/svn/svn.c (revision 1509957) +++ subversion/svn/svn.c (working copy) @@ -1340,6 +1340,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table " before it is modified. Makes the working copy file read-only\n" " when it is not locked. Use 'svn propdel svn:needs-lock PATH...'\n" " to clear.\n" + " svn:use-commit-times - If present, make the timestamp of the file\n" + " to commit time. Use 'svn propdel svn:use-commit-times PATH...' to clear.\n" "\n" " Subversion recognizes the following special versioned properties on a\n" " directory:\n" Index: subversion/tests/cmdline/checkout_tests.py =================================================================== --- subversion/tests/cmdline/checkout_tests.py (revision 1509957) +++ subversion/tests/cmdline/checkout_tests.py (working copy) @@ -1102,7 +1102,61 @@ def checkout_wc_from_drive(sbox): subprocess.call(['subst', '/D', drive +':']) #---------------------------------------------------------------------- +# Test checking out with svn:use-commit-times +def checkout_with_use_commit_times(sbox): + "checkout with svn:use-commit-times" + sbox.build() + + # Create the revprop-change hook for this test + svntest.actions.enable_revprop_changes(sbox.repo_dir) + + wc_dir = sbox.wc_dir + iota_path = sbox.ospath('iota') + + svntest.actions.set_prop('svn:use-commit-times', '*', iota_path) + + # Create expected output tree. + expected_output = svntest.wc.State(wc_dir, { + 'iota' : Item(verb='Sending'), + }) + + # Commit the one file. + svntest.actions.run_and_verify_commit(wc_dir, + expected_output, + None, + None, + iota_path) + + svntest.actions.run_and_verify_svn(None, None, [], + 'propset', '--revprop', '-r2', + 'svn:date', '2001-01-01T00:00:00.000000Z', + sbox.wc_dir) + + # now checkout the repo in another folder + checkout_target = sbox.add_wc_path('checkout') + os.mkdir(checkout_target) + + expected_output = svntest.main.greek_state.copy() + expected_output.wc_dir = checkout_target + expected_output.tweak(status='A ', contents=None) + expected_wc = svntest.main.greek_state.copy() + + svntest.actions.run_and_verify_checkout(sbox.repo_url, + checkout_target, + expected_output, + expected_wc) + secs_svn_date = time.mktime( (2001, 1, 1, 0, 0, 0, 0, 0, 0) ) + secs_svn_date -= time.timezone + new_iota = os.path.join(checkout_target, 'iota') + mtime = os.path.getmtime(new_iota) + + # check whether the timestamp of the file is expected value + if secs_svn_date != mtime: + raise svntest.Failure + +#---------------------------------------------------------------------- + # list all tests here, starting with None: test_list = [ None, checkout_with_obstructions, @@ -1118,7 +1172,8 @@ test_list = [ None, checkout_peg_rev, checkout_peg_rev_date, co_with_obstructing_local_adds, - checkout_wc_from_drive + checkout_wc_from_drive, + checkout_with_use_commit_times ] if __name__ == "__main__": Index: subversion/tests/cmdline/export_tests.py =================================================================== --- subversion/tests/cmdline/export_tests.py (revision 1509957) +++ subversion/tests/cmdline/export_tests.py (working copy) @@ -27,6 +27,7 @@ # General modules import os import tempfile +import time # Our testing module import svntest @@ -964,6 +965,63 @@ def export_custom_keywords(sbox): if open(export_file).read() != ''.join(alpha_content): raise svntest.Failure("wrong keyword expansion") + +#---------------------------------------------------------------------- +# Test exporting out with svn:use-commit-times +def export_with_use_commit_times(sbox): + "export with svn:use-commit-times" + + sbox.build() + + # Create the revprop-change hook for this test + svntest.actions.enable_revprop_changes(sbox.repo_dir) + + wc_dir = sbox.wc_dir + iota_path = sbox.ospath('iota') + + svntest.actions.set_prop('svn:use-commit-times', '*', iota_path) + + # Create expected output tree. + expected_output = svntest.wc.State(wc_dir, { + 'iota' : Item(verb='Sending'), + }) + + # Commit the one file. + svntest.actions.run_and_verify_commit(wc_dir, + expected_output, + None, + None, + iota_path) + + svntest.actions.run_and_verify_svn(None, None, [], + 'propset', '--revprop', '-r2', + 'svn:date', '2001-01-01T00:00:00.000000Z', + sbox.wc_dir) + + # now export the repo in another folder + export_target = sbox.add_wc_path('export') + expected_disk = svntest.main.greek_state.copy() + export_target = sbox.add_wc_path('export') + + expected_output = svntest.main.greek_state.copy() + expected_output.wc_dir = export_target + expected_output.desc[''] = Item() + expected_output.tweak(contents=None, status='A ') + + svntest.actions.run_and_verify_export(sbox.repo_url, + export_target, + expected_output, + expected_disk) + + secs_svn_date = time.mktime( (2001, 1, 1, 0, 0, 0, 0, 0, 0) ) + secs_svn_date -= time.timezone + new_iota = os.path.join(export_target, 'iota') + mtime = os.path.getmtime(new_iota) + + # check whether the timestamp of the file is expected value + if secs_svn_date != mtime: + raise svntest.Failure + ######################################################################## # Run the tests @@ -998,6 +1056,7 @@ test_list = [ None, export_to_current_dir, export_file_overwrite_with_force, export_custom_keywords, + export_with_use_commit_times, ] if __name__ == '__main__': Index: subversion/tests/cmdline/prop_tests.py =================================================================== --- subversion/tests/cmdline/prop_tests.py (revision 1509957) +++ subversion/tests/cmdline/prop_tests.py (working copy) @@ -901,6 +901,13 @@ def prop_value_conversions(sbox): svntest.actions.set_prop('svn:executable', pval, mu_path, "svn: warning: W125005.*use 'svn propdel'") + # svn:use-commit-times value should be forced to a '*' + svntest.actions.set_prop('svn:use-commit-times', 'foo', iota_path) + svntest.actions.set_prop('svn:use-commit-times', '*', lambda_path) + for pval in (' ', '', 'no', 'off', 'false'): + svntest.actions.set_prop('svn:use-commit-times', pval, mu_path, + "svn: warning: W125005.*use 'svn propdel'") + # Anything else should be untouched svntest.actions.set_prop('svn:some-prop', 'bar', lambda_path, force=True) svntest.actions.set_prop('svn:some-prop', ' bar baz', mu_path, force=True) @@ -945,6 +952,11 @@ def prop_value_conversions(sbox): svntest.actions.check_prop('svn:executable', lambda_path, ['*']) svntest.actions.check_prop('svn:executable', mu_path, ['*']) + # Check svn:use-commit-times + svntest.actions.check_prop('svn:use-commit-times', iota_path, ['*']) + svntest.actions.check_prop('svn:use-commit-times', lambda_path, ['*']) + svntest.actions.check_prop('svn:use-commit-times', mu_path, ['*']) + # Check other props svntest.actions.check_prop('svn:some-prop', lambda_path, ['bar']) svntest.actions.check_prop('svn:some-prop', mu_path, [' bar baz']) Index: subversion/tests/cmdline/update_tests.py =================================================================== --- subversion/tests/cmdline/update_tests.py (revision 1509957) +++ subversion/tests/cmdline/update_tests.py (working copy) @@ -6779,6 +6779,111 @@ def update_child_below_add(sbox): sbox.ospath('A/B/E')) +#---------------------------------------------------------------------- +# Test updating out with svn:use-commit-times +def update_with_use_commit_times(sbox): + "update with svn:use-commit-times" + + sbox.build() + + # Create the revprop-change hook for this test + svntest.actions.enable_revprop_changes(sbox.repo_dir) + + wc_dir = sbox.wc_dir + iota_path = sbox.ospath('iota') + + # checkout the repo before setting 'svn:use-commit-times' + checkout_target = sbox.add_wc_path('checkout') + os.mkdir(checkout_target) + + expected_output = svntest.main.greek_state.copy() + expected_output.wc_dir = checkout_target + expected_output.tweak(status='A ', contents=None) + expected_wc = svntest.main.greek_state.copy() + + # checkout for updating before setting 'svn:use-commit-times' + svntest.actions.run_and_verify_checkout(sbox.repo_url, + checkout_target, + expected_output, + expected_wc) + + # set 'svn:use-commit-times' to wc + svntest.actions.set_prop('svn:use-commit-times', '*', iota_path) + + # Create expected output tree. + expected_output = svntest.wc.State(wc_dir, { + 'iota' : Item(verb='Sending'), + }) + + # Commit the one file. + svntest.actions.run_and_verify_commit(wc_dir, + expected_output, + None, + None, + iota_path) + + svntest.actions.run_and_verify_svn(None, None, [], + 'propset', '--revprop', '-r1', + 'svn:date', '2001-01-01T00:00:00.000000Z', + sbox.wc_dir) + + svntest.actions.run_and_verify_svn(None, None, [], + 'propset', '--revprop', '-r2', + 'svn:date', '2001-01-01T00:00:00.000000Z', + sbox.wc_dir) + + # Update + expected_output = svntest.wc.State(checkout_target, { + 'iota' : Item(status=' U'), + }) + expected_output.wc_dir = checkout_target + + expected_disk = svntest.main.greek_state.copy() + expected_disk.wc_dir = checkout_target + + expected_status = svntest.actions.get_virginal_state(checkout_target, 1) + expected_status.tweak('iota', wc_rev=2) + + svntest.actions.run_and_verify_update(checkout_target, + None, + None, + None, + None, None, None, None, None, 0 ) + + secs_svn_date = time.mktime( (2001, 1, 1, 0, 0, 0, 0, 0, 0) ) + secs_svn_date -= time.timezone + new_iota = os.path.join(checkout_target, 'iota') + mtime = os.path.getmtime(new_iota) + + # check whether the timestamp of the file is expected value + if secs_svn_date != mtime: + raise svntest.Failure + + # remove 'svn:use-commit-times' property test + svntest.main.run_svn(None, 'propdel', 'svn:use-commit-times', iota_path) + svntest.actions.run_and_verify_commit(wc_dir, + None, + None, + None, + iota_path) + # get current time before update + time_before_update = time.time() + + # update + svntest.actions.run_and_verify_update(checkout_target, + None, + None, + None, + None, None, None, None, None, 0 ) + # get current time after update + time_before_after = time.time() + + mtime = os.path.getmtime(new_iota) + + # check whether the timestamp of the file is between 'before update' and 'after update' + if time_before_update > mtime or mtime > time_before_after: + raise svntest.Failure + ####################################################################### # Run the tests @@ -6865,6 +6970,7 @@ test_list = [ None, update_moved_away, bump_below_tree_conflict, update_child_below_add, + update_with_use_commit_times, ] if __name__ == '__main__':