diff --git a/.gitignore b/.gitignore
index ced8619..d7122ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ subversion-1.6.12.tar.bz2
/subversion-1.?.?
/*.asc
/subversion-1.?.??
+/subversion-1.7.4.tar.bz2
diff --git a/sources b/sources
index 4779bae..ed748fb 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-867fb0a5db00710cf8dce0bdfa094b3b subversion-1.7.3.tar.bz2
+99ace9433be21a4793fc1cdbf2fdea09 subversion-1.7.4.tar.bz2
diff --git a/subversion-1.7.3-hashorder.patch b/subversion-1.7.3-hashorder.patch
deleted file mode 100644
index c0597c1..0000000
--- a/subversion-1.7.3-hashorder.patch
+++ /dev/null
@@ -1,958 +0,0 @@
-
-1.7.x branch:
-
-http://svn.apache.org/viewvc?rev=1293811&view=rev
-http://svn.apache.org/viewvc?rev=1293812&view=rev
-
-trunk:
-
-http://svn.apache.org/viewvc?rev=1292248&view=rev
-http://svn.apache.org/viewvc?rev=1292260&view=rev
-http://svn.apache.org/viewvc?rev=1292296&view=rev
-http://svn.apache.org/viewvc?rev=1292322&view=rev
-
---- subversion-1.7.3/subversion/bindings/swig/python/tests/repository.py.hashorder
-+++ subversion-1.7.3/subversion/bindings/swig/python/tests/repository.py
-@@ -170,9 +170,11 @@ class SubversionRepositoryTestCase(unitt
- repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
- _authz_callback, 1, 1, 0, 0)
-
-- # Check results
-- self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n")
-- self.assertEqual(editor.textdeltas[1].new_data, "A test.\n")
-+ # Check results.
-+ # Ignore the order in which the editor delivers the two sibling files.
-+ self.assertEqual(set([editor.textdeltas[0].new_data,
-+ editor.textdeltas[1].new_data]),
-+ set(["This is a test.\n", "A test.\n"]))
- self.assertEqual(len(editor.textdeltas), 2)
-
- def test_retrieve_and_change_rev_prop(self):
---- subversion-1.7.3/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py.hashorder
-+++ subversion-1.7.3/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py
-@@ -264,30 +264,50 @@ class SubversionRepositoryTestCase(unitt
-
- def test_diff_dir_different_revs(self):
- diffs = self.repos.get_deltas('trunk', 4, 'trunk', 8)
-- self._cmp_diff((None, ('trunk/dir1/dir2', 8),
-- (Node.DIRECTORY, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('trunk/dir1/dir3', 8),
-- (Node.DIRECTORY, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('trunk/README2.txt', 6),
-- (Node.FILE, Changeset.ADD)), diffs.next())
-- self._cmp_diff((('trunk/dir2', 4), None,
-- (Node.DIRECTORY, Changeset.DELETE)), diffs.next())
-- self._cmp_diff((('trunk/dir3', 4), None,
-- (Node.DIRECTORY, Changeset.DELETE)), diffs.next())
-+ expected = [
-+ (None, ('trunk/README2.txt', 6),
-+ (Node.FILE, Changeset.ADD)),
-+ (None, ('trunk/dir1/dir2', 8),
-+ (Node.DIRECTORY, Changeset.ADD)),
-+ (None, ('trunk/dir1/dir3', 8),
-+ (Node.DIRECTORY, Changeset.ADD)),
-+ (('trunk/dir2', 4), None,
-+ (Node.DIRECTORY, Changeset.DELETE)),
-+ (('trunk/dir3', 4), None,
-+ (Node.DIRECTORY, Changeset.DELETE)),
-+ ]
-+ actual = [diffs.next() for i in range(5)]
-+ actual = sorted(actual,
-+ key=lambda diff: ((diff[0] or diff[1]).path,
-+ (diff[0] or diff[1]).rev))
-+ self.assertEqual(len(expected), len(actual))
-+ for e,a in zip(expected, actual):
-+ self._cmp_diff(e,a)
- self.assertRaises(StopIteration, diffs.next)
-
- def test_diff_dir_different_dirs(self):
- diffs = self.repos.get_deltas('trunk', 1, 'branches/v1x', 12)
-- self._cmp_diff((None, ('branches/v1x/dir1', 12),
-- (Node.DIRECTORY, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('branches/v1x/dir1/dir2', 12),
-- (Node.DIRECTORY, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('branches/v1x/dir1/dir3', 12),
-- (Node.DIRECTORY, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('branches/v1x/README.txt', 12),
-- (Node.FILE, Changeset.ADD)), diffs.next())
-- self._cmp_diff((None, ('branches/v1x/README2.txt', 12),
-- (Node.FILE, Changeset.ADD)), diffs.next())
-+ expected = [
-+ (None, ('branches/v1x/README.txt', 12),
-+ (Node.FILE, Changeset.ADD)),
-+ (None, ('branches/v1x/README2.txt', 12),
-+ (Node.FILE, Changeset.ADD)),
-+ (None, ('branches/v1x/dir1', 12),
-+ (Node.DIRECTORY, Changeset.ADD)),
-+ (None, ('branches/v1x/dir1/dir2', 12),
-+ (Node.DIRECTORY, Changeset.ADD)),
-+ (None, ('branches/v1x/dir1/dir3', 12),
-+ (Node.DIRECTORY, Changeset.ADD)),
-+ ]
-+ actual = [diffs.next() for i in range(5)]
-+ actual = sorted(actual, key=lambda diff: (diff[1].path, diff[1].rev))
-+ # for e,a in zip(expected, actual):
-+ # t.write("%r\n" % (e,))
-+ # t.write("%r\n" % ((None, (a[1].path, a[1].rev), (a[2], a[3])),) )
-+ # t.write('\n')
-+ self.assertEqual(len(expected), len(actual))
-+ for e,a in zip(expected, actual):
-+ self._cmp_diff(e,a)
- self.assertRaises(StopIteration, diffs.next)
-
- def test_diff_dir_no_change(self):
---- subversion-1.7.3/subversion/bindings/swig/python/tests/wc.py.hashorder
-+++ subversion-1.7.3/subversion/bindings/swig/python/tests/wc.py
-@@ -216,8 +216,9 @@ class SubversionWorkingCopyTestCase(unit
-
- def test_entries_read(self):
- entries = wc.entries_read(self.wc, True)
--
-- self.assertEqual(['', 'tags', 'branches', 'trunk'], list(entries.keys()))
-+ keys = list(entries.keys())
-+ keys.sort()
-+ self.assertEqual(['', 'branches', 'tags', 'trunk'], keys)
-
- def test_get_ignores(self):
- self.assert_(isinstance(wc.get_ignores(None, self.wc), list))
---- subversion-1.7.3/subversion/tests/cmdline/diff_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/diff_tests.py
-@@ -1165,8 +1165,10 @@ def diff_base_to_repos(sbox):
- if not re_infoline.match(line):
- list2.append(line)
-
-- if list1 != list2:
-- raise svntest.Failure
-+ # Two files in diff may be in any order.
-+ list1 = svntest.verify.UnorderedOutput(list1)
-+
-+ svntest.verify.compare_and_display_lines('', '', list1, list2)
-
-
- #----------------------------------------------------------------------
-@@ -3590,6 +3592,9 @@ def diff_git_empty_files(sbox):
- ] + make_git_diff_header(iota_path, "iota", "revision 2", "working copy",
- delete=True, text_changes=False)
-
-+ # Two files in diff may be in any order.
-+ expected_output = svntest.verify.UnorderedOutput(expected_output)
-+
- svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
- '--git', wc_dir)
-
-@@ -3630,6 +3635,9 @@ def diff_git_with_props(sbox):
- make_diff_prop_header("iota") + \
- make_diff_prop_added("svn:keywords", "Id")
-
-+ # Files in diff may be in any order.
-+ expected_output = svntest.verify.UnorderedOutput(expected_output)
-+
- svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
- '--git', wc_dir)
-
---- subversion-1.7.3/subversion/tests/cmdline/lock_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/lock_tests.py
-@@ -1172,6 +1172,8 @@ def repos_lock_with_info(sbox):
-
-
- #----------------------------------------------------------------------
-+@Issue(4126)
-+@Skip(svntest.main.is_ra_type_dav_serf) # Issue 4126 unpredictable result
- def unlock_already_unlocked_files(sbox):
- "(un)lock set of files, one already (un)locked"
-
---- subversion-1.7.3/subversion/tests/cmdline/patch_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/patch_tests.py
-@@ -2822,12 +2822,13 @@ def patch_prop_offset(sbox):
-
- os.chdir(wc_dir)
-
-- expected_output = [
-+ # Changing two properties so output order not well defined.
-+ expected_output = svntest.verify.UnorderedOutput([
- ' U iota\n',
- '> applied hunk ## -6,6 +6,9 ## with offset -1 (prop1)\n',
- '> applied hunk ## -14,11 +17,8 ## with offset 4 (prop1)\n',
- '> applied hunk ## -5,6 +5,7 ## with offset -3 (prop2)\n',
-- ]
-+ ])
-
- expected_disk = svntest.main.greek_state.copy()
- expected_disk.tweak('iota', props = {'prop1' : prop1_content,
---- subversion-1.7.3/subversion/tests/cmdline/stat_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/stat_tests.py
-@@ -924,38 +924,13 @@ def status_in_xml(sbox):
- else:
- raise svntest.Failure
-
-- template = ['\n',
-- "\n",
-- "\n" % (file_path),
-- "\n" % (file_path),
-- "\n",
-- "\n",
-- "%s\n" % svntest.main.wc_author,
-- time_str,
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- ]
-+ expected_entries = {file_path : {'wcprops' : 'none',
-+ 'wcitem' : 'modified',
-+ 'wcrev' : '1',
-+ 'crev' : '1',
-+ 'author' : svntest.main.wc_author}}
-
-- exit_code, output, error = svntest.actions.run_and_verify_svn(None, None, [],
-- 'status',
-- file_path,
-- '--xml', '-u')
--
-- for i in range(0, len(output)):
-- if output[i] != template[i]:
-- print("ERROR: expected: %s actual: %s" % (template[i], output[i]))
-- raise svntest.Failure
-+ svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u')
-
- svntest.actions.run_and_verify_svn(None, None, [],
- 'cp', '-m', 'repo-to-repo copy',
-@@ -964,36 +939,12 @@ def status_in_xml(sbox):
-
- file_path = sbox.ospath('iota2')
-
-- template = ['\n',
-- "\n",
-- "\n" % (file_path),
-- "\n" % (file_path),
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- ]
-+ expected_entries = {file_path : {'wcprops' : 'none',
-+ 'wcitem' : 'none',
-+ 'rprops' : 'none',
-+ 'ritem' : 'added'}}
-
-- exit_code, output, error = svntest.actions.run_and_verify_svn(None, None, [],
-- 'status',
-- file_path,
-- '--xml', '-u')
--
-- for i in range(0, len(output)):
-- if output[i] != template[i]:
-- print("ERROR: expected: %s actual: %s" % (template[i], output[i]))
-- raise svntest.Failure
-+ svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u')
-
- #----------------------------------------------------------------------
-
-@@ -1269,53 +1220,23 @@ def status_update_with_incoming_props(sb
- else:
- raise svntest.Failure
-
-- xout = ['\n',
-- "\n",
-- "\n" % (wc_dir),
-- "\n" % (A_path),
-- "\n",
-- "\n",
-- "%s\n" % svntest.main.wc_author,
-- time_str,
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n" % (wc_dir),
-- "\n",
-- "\n",
-- "%s\n" % svntest.main.wc_author,
-- time_str,
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",
-- "\n",]
--
-- exit_code, output, error = svntest.actions.run_and_verify_svn(None, xout, [],
-- 'status',
-- wc_dir,
-- '--xml', '-uN')
-+ expected_entries ={wc_dir : {'wcprops' : 'none',
-+ 'wcitem' : 'normal',
-+ 'wcrev' : '1',
-+ 'crev' : '1',
-+ 'author' : svntest.main.wc_author,
-+ 'rprops' : 'modified',
-+ 'ritem' : 'none'},
-+ A_path : {'wcprops' : 'none',
-+ 'wcitem' : 'normal',
-+ 'wcrev' : '1',
-+ 'crev' : '1',
-+ 'author' : svntest.main.wc_author,
-+ 'rprops' : 'modified',
-+ 'ritem' : 'none'},
-+ }
-+
-+ svntest.actions.run_and_verify_status_xml(expected_entries, wc_dir, '-uN')
-
- # more incoming prop updates.
- def status_update_verbose_with_incoming_props(sbox):
---- subversion-1.7.3/subversion/tests/cmdline/svnlook_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/svnlook_tests.py
-@@ -117,35 +117,39 @@ def test_misc(sbox):
- # the 'svnlook tree --full-paths' output if demanding the whole repository
- treelist = run_svnlook('tree', repo_dir)
- treelistfull = run_svnlook('tree', '--full-paths', repo_dir)
-+
- path = ''
-- n = 0
-+ treelistexpand = []
- for entry in treelist:
- len1 = len(entry)
- len2 = len(entry.lstrip())
-- path = path[0:2*(len1-len2)-1] + entry.strip()
-- test = treelistfull[n].rstrip()
-- if n != 0:
-- test = "/" + test
-- if not path == test:
-- print("Unexpected result from tree with --full-paths:")
-- print(" entry : %s" % entry.rstrip())
-- print(" with --full-paths: %s" % treelistfull[n].rstrip())
-- raise svntest.Failure
-- n = n + 1
-+ path = path[0:2*(len1-len2)-1] + entry.strip() + '\n'
-+ if path == '/\n':
-+ treelistexpand.append(path)
-+ else:
-+ treelistexpand.append(path[1:])
-+
-+ treelistexpand = svntest.verify.UnorderedOutput(treelistexpand)
-+ svntest.verify.compare_and_display_lines('Unexpected result from tree', '',
-+ treelistexpand, treelistfull)
-
- # check if the 'svnlook tree' output is the ending of
- # the 'svnlook tree --full-paths' output if demanding
- # any part of the repository
-- n = 0
- treelist = run_svnlook('tree', repo_dir, '/A/B')
- treelistfull = run_svnlook('tree', '--full-paths', repo_dir, '/A/B')
-+
-+ path = ''
-+ treelistexpand = []
- for entry in treelist:
-- if not treelistfull[n].endswith(entry.lstrip()):
-- print("Unexpected result from tree with --full-paths:")
-- print(" entry : %s" % entry.rstrip())
-- print(" with --full-paths: %s" % treelistfull[n].rstrip())
-- raise svntest.Failure
-- n = n + 1
-+ len1 = len(entry)
-+ len2 = len(entry.lstrip())
-+ path = path[0:2*(len1-len2)] + entry.strip() + '\n'
-+ treelistexpand.append('/A/' + path)
-+
-+ treelistexpand = svntest.verify.UnorderedOutput(treelistexpand)
-+ svntest.verify.compare_and_display_lines('Unexpected result from tree', '',
-+ treelistexpand, treelistfull)
-
- treelist = run_svnlook('tree', repo_dir, '/')
- if treelist[0] != '/\n':
-@@ -695,7 +699,7 @@ fp.close()"""
- # internal property, not really expected
- ' svn:check-locks\n',
- ' bogus_rev_prop\n', ' svn:date\n']
-- verify_logfile(logfilepath, expected_data)
-+ verify_logfile(logfilepath, svntest.verify.UnorderedOutput(expected_data))
-
- ########################################################################
- # Run the tests
---- subversion-1.7.3/subversion/tests/cmdline/svnrdump_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/svnrdump_tests.py
-@@ -70,8 +70,31 @@ def build_repos(sbox):
- # Create an empty repository.
- svntest.main.create_repos(sbox.repo_dir)
-
-+def compare_repos_dumps(svnrdump_sbox, svnadmin_dumpfile):
-+ """Compare two dumpfiles, one created from SVNRDUMP_SBOX, and other given
-+ by SVNADMIN_DUMPFILE. The dumpfiles do not need to match linewise, as the
-+ SVNADMIN_DUMPFILE contents will first be loaded into a repository and then
-+ re-dumped to do the match, which should generate the same dumpfile as
-+ dumping SVNRDUMP_SBOX."""
-+
-+ svnrdump_contents = svntest.actions.run_and_verify_dump(
-+ svnrdump_sbox.repo_dir)
-+
-+ svnadmin_sbox = svnrdump_sbox.clone_dependent()
-+ svntest.main.safe_rmtree(svnadmin_sbox.repo_dir)
-+ svntest.main.create_repos(svnadmin_sbox.repo_dir)
-+
-+ svntest.actions.run_and_verify_load(svnadmin_sbox.repo_dir, svnadmin_dumpfile)
-+
-+ svnadmin_contents = svntest.actions.run_and_verify_dump(
-+ svnadmin_sbox.repo_dir)
-+
-+ svntest.verify.compare_dump_files(
-+ "Dump files", "DUMP", svnadmin_contents, svnrdump_contents)
-+
- def run_dump_test(sbox, dumpfile_name, expected_dumpfile_name = None,
-- subdir = None, bypass_prop_validation = False):
-+ subdir = None, bypass_prop_validation = False,
-+ ignore_base_checksums = False):
- """Load a dumpfile using 'svnadmin load', dump it with 'svnrdump
- dump' and check that the same dumpfile is produced or that
- expected_dumpfile_name is produced if provided. Additionally, the
-@@ -107,12 +130,21 @@ def run_dump_test(sbox, dumpfile_name, e
- svnadmin_dumpfile = open(os.path.join(svnrdump_tests_dir,
- expected_dumpfile_name),
- 'rb').readlines()
-+ # Compare the output from stdout
-+ if ignore_base_checksums:
-+ svnadmin_dumpfile = [l for l in svnadmin_dumpfile
-+ if not l.startswith('Text-delta-base-md5')]
-+ svnrdump_dumpfile = [l for l in svnrdump_dumpfile
-+ if not l.startswith('Text-delta-base-md5')]
-+
- svnadmin_dumpfile = svntest.verify.UnorderedOutput(svnadmin_dumpfile)
-
-- # Compare the output from stdout
-- svntest.verify.compare_and_display_lines(
-- "Dump files", "DUMP", svnadmin_dumpfile, svnrdump_dumpfile,
-- None, mismatched_headers_re)
-+ svntest.verify.compare_and_display_lines(
-+ "Dump files", "DUMP", svnadmin_dumpfile, svnrdump_dumpfile,
-+ None, mismatched_headers_re)
-+
-+ else:
-+ compare_repos_dumps(sbox, svnadmin_dumpfile)
-
- def run_load_test(sbox, dumpfile_name, expected_dumpfile_name = None,
- expect_deltas = True):
-@@ -155,9 +187,12 @@ def run_load_test(sbox, dumpfile_name, e
- expected_dumpfile_name),
- 'rb').readlines()
-
-- # Compare the output from stdout
-- svntest.verify.compare_and_display_lines(
-- "Dump files", "DUMP", svnrdump_dumpfile, svnadmin_dumpfile)
-+ # Compare the output from stdout
-+ svntest.verify.compare_and_display_lines(
-+ "Dump files", "DUMP", svnrdump_dumpfile, svnadmin_dumpfile)
-+
-+ else:
-+ compare_repos_dumps(sbox, svnrdump_dumpfile)
-
- ######################################################################
- # Tests
-@@ -345,7 +380,7 @@ def copy_bad_line_endings2_dump(sbox):
- "dump: non-LF line endings in svn:* props"
- run_dump_test(sbox, "copy-bad-line-endings2.dump",
- expected_dumpfile_name="copy-bad-line-endings2.expected.dump",
-- bypass_prop_validation=True)
-+ bypass_prop_validation=True, ignore_base_checksums=True)
-
- @Skip(svntest.main.is_ra_type_dav_serf)
- def commit_a_copy_of_root_dump(sbox):
---- subversion-1.7.3/subversion/tests/cmdline/svnsync_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/svnsync_tests.py
-@@ -222,7 +222,7 @@ def verify_mirror(dest_sbox, exp_dump_fi
- # Create a dump file from the mirror repository.
- dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
-
-- svntest.verify.compare_and_display_lines(
-+ svntest.verify.compare_dump_files(
- "Dump files", "DUMP", exp_dump_file_contents, dest_dump)
-
- def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
---- subversion-1.7.3/subversion/tests/cmdline/svntest/actions.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/svntest/actions.py
-@@ -556,7 +556,8 @@ class LogEntry:
- self.revprops = revprops
-
- def assert_changed_paths(self, changed_paths):
-- """Not implemented, so just raises svntest.Failure.
-+ """Assert that changed_paths is the same as this entry's changed_paths
-+ Raises svntest.Failure if not.
- """
- raise Failure('NOT IMPLEMENTED')
-
-@@ -1079,13 +1080,21 @@ def run_and_verify_merge(dir, rev1, rev2
- if dry_run and merge_diff_out != out_dry:
- # Due to the way ra_serf works, it's possible that the dry-run and
- # real merge operations did the same thing, but the output came in
-- # a different order. Let's see if maybe that's the case.
-+ # a different order. Let's see if maybe that's the case by comparing
-+ # the outputs as unordered sets rather than as lists.
-+ #
-+ # This now happens for other RA layers with modern APR because the
-+ # hash order now varies.
- #
-- # NOTE: Would be nice to limit this dance to serf tests only, but...
-- out_copy = merge_diff_out[:]
-- out_dry_copy = out_dry[:]
-- out_copy.sort()
-- out_dry_copy.sort()
-+ # The different orders of the real and dry-run merges may cause
-+ # the "Merging rX through rY into" lines to be duplicated a
-+ # different number of times in the two outputs. The list-set
-+ # conversion removes duplicates so these differences are ignored.
-+ # It also removes "U some/path" duplicate lines. Perhaps we
-+ # should avoid that?
-+ out_copy = set(merge_diff_out[:])
-+ out_dry_copy = set(out_dry[:])
-+
- if out_copy != out_dry_copy:
- print("=============================================================")
- print("Merge outputs differ")
-@@ -1198,16 +1207,11 @@ def run_and_verify_patch(dir, patch_path
- raise verify.SVNUnexpectedStderr
-
- if dry_run and out != out_dry:
-- print("=============================================================")
-- print("Outputs differ")
-- print("'svn patch --dry-run' output:")
-- for x in out_dry:
-- sys.stdout.write(x)
-- print("'svn patch' output:")
-- for x in out:
-- sys.stdout.write(x)
-- print("=============================================================")
-- raise main.SVNUnmatchedError
-+ # APR hash order means the output order can vary, assume everything is OK
-+ # if only the order changes.
-+ out_dry_expected = svntest.verify.UnorderedOutput(out)
-+ verify.compare_and_display_lines('dry-run patch output not as expected',
-+ '', out_dry_expected, out_dry)
-
- def missing_skip(a, b):
- print("=============================================================")
-@@ -1230,7 +1234,8 @@ def run_and_verify_patch(dir, patch_path
-
- # when the expected output is a list, we want a line-by-line
- # comparison to happen instead of a tree comparison
-- if isinstance(output_tree, list):
-+ if (isinstance(output_tree, list)
-+ or isinstance(output_tree, verify.UnorderedOutput)):
- verify.verify_outputs(None, out, err, output_tree, error_re_string)
- output_tree = None
-
-@@ -1503,6 +1508,56 @@ def run_and_verify_unquiet_status(wc_dir
- tree.dump_tree_script(actual, wc_dir_name + os.sep)
- raise
-
-+def run_and_verify_status_xml(expected_entries = [],
-+ *args):
-+ """ Run 'status --xml' with arguments *ARGS. If successful the output
-+ is parsed into an XML document and will be verified by comparing against
-+ EXPECTED_ENTRIES.
-+ """
-+
-+ exit_code, output, errput = run_and_verify_svn(None, None, [],
-+ 'status', '--xml', *args)
-+
-+ if len(errput) > 0:
-+ raise Failure
-+
-+ doc = parseString(''.join(output))
-+ entries = doc.getElementsByTagName('entry')
-+
-+ def getText(nodelist):
-+ rc = []
-+ for node in nodelist:
-+ if node.nodeType == node.TEXT_NODE:
-+ rc.append(node.data)
-+ return ''.join(rc)
-+
-+ actual_entries = {}
-+ for entry in entries:
-+ wcstatus = entry.getElementsByTagName('wc-status')[0]
-+ commit = entry.getElementsByTagName('commit')
-+ author = entry.getElementsByTagName('author')
-+ rstatus = entry.getElementsByTagName('repos-status')
-+
-+ actual_entry = {'wcprops' : wcstatus.getAttribute('props'),
-+ 'wcitem' : wcstatus.getAttribute('item'),
-+ }
-+ if wcstatus.hasAttribute('revision'):
-+ actual_entry['wcrev'] = wcstatus.getAttribute('revision')
-+ if (commit):
-+ actual_entry['crev'] = commit[0].getAttribute('revision')
-+ if (author):
-+ actual_entry['author'] = getText(author[0].childNodes)
-+ if (rstatus):
-+ actual_entry['rprops'] = rstatus[0].getAttribute('props')
-+ actual_entry['ritem'] = rstatus[0].getAttribute('item')
-+
-+ actual_entries[entry.getAttribute('path')] = actual_entry
-+
-+ if expected_entries != actual_entries:
-+ raise Failure('\n' + '\n'.join(difflib.ndiff(
-+ pprint.pformat(expected_entries).splitlines(),
-+ pprint.pformat(actual_entries).splitlines())))
-+
- def run_and_verify_diff_summarize_xml(error_re_string = [],
- expected_prefix = None,
- expected_paths = [],
---- subversion-1.7.3/subversion/tests/cmdline/svntest/verify.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/svntest/verify.py
-@@ -25,7 +25,8 @@
- ######################################################################
-
- import re, sys
--from difflib import unified_diff
-+from difflib import unified_diff, ndiff
-+import pprint
-
- import svntest
-
-@@ -68,6 +69,10 @@ class SVNIncorrectDatatype(SVNUnexpected
- run_and_verify_* API"""
- pass
-
-+class SVNDumpParseError(svntest.Failure):
-+ """Exception raised if parsing a dump file fails"""
-+ pass
-+
-
- ######################################################################
- # Comparison of expected vs. actual output
-@@ -397,3 +402,193 @@ def verify_exit_code(message, actual, ex
- display_lines(message, "Exit Code",
- str(expected) + '\n', str(actual) + '\n')
- raise raisable
-+
-+# A simple dump file parser. While sufficient for the current
-+# testsuite it doesn't cope with all valid dump files.
-+class DumpParser:
-+ def __init__(self, lines):
-+ self.current = 0
-+ self.lines = lines
-+ self.parsed = {}
-+
-+ def parse_line(self, regex, required=True):
-+ m = re.match(regex, self.lines[self.current])
-+ if not m:
-+ if required:
-+ raise SVNDumpParseError("expected '%s' at line %d\n%s"
-+ % (regex, self.current,
-+ self.lines[self.current]))
-+ else:
-+ return None
-+ self.current += 1
-+ return m.group(1)
-+
-+ def parse_blank(self, required=True):
-+ if self.lines[self.current] != '\n': # Works on Windows
-+ if required:
-+ raise SVNDumpParseError("expected blank at line %d\n%s"
-+ % (self.current, self.lines[self.current]))
-+ else:
-+ return False
-+ self.current += 1
-+ return True
-+
-+ def parse_format(self):
-+ return self.parse_line('SVN-fs-dump-format-version: ([0-9]+)$')
-+
-+ def parse_uuid(self):
-+ return self.parse_line('UUID: ([0-9a-z-]+)$')
-+
-+ def parse_revision(self):
-+ return self.parse_line('Revision-number: ([0-9]+)$')
-+
-+ def parse_prop_length(self, required=True):
-+ return self.parse_line('Prop-content-length: ([0-9]+)$', required)
-+
-+ def parse_content_length(self, required=True):
-+ return self.parse_line('Content-length: ([0-9]+)$', required)
-+
-+ def parse_path(self):
-+ path = self.parse_line('Node-path: (.+)$', required=False)
-+ if not path and self.lines[self.current] == 'Node-path: \n':
-+ self.current += 1
-+ path = ''
-+ return path
-+
-+ def parse_kind(self):
-+ return self.parse_line('Node-kind: (.+)$', required=False)
-+
-+ def parse_action(self):
-+ return self.parse_line('Node-action: ([0-9a-z-]+)$')
-+
-+ def parse_copyfrom_rev(self):
-+ return self.parse_line('Node-copyfrom-rev: ([0-9]+)$', required=False)
-+
-+ def parse_copyfrom_path(self):
-+ path = self.parse_line('Node-copyfrom-path: (.+)$', required=False)
-+ if not path and self.lines[self.current] == 'Node-copyfrom-path: \n':
-+ self.current += 1
-+ path = ''
-+ return path
-+
-+ def parse_copy_md5(self):
-+ return self.parse_line('Text-copy-source-md5: ([0-9a-z]+)$', required=False)
-+
-+ def parse_copy_sha1(self):
-+ return self.parse_line('Text-copy-source-sha1: ([0-9a-z]+)$', required=False)
-+
-+ def parse_text_md5(self):
-+ return self.parse_line('Text-content-md5: ([0-9a-z]+)$', required=False)
-+
-+ def parse_text_sha1(self):
-+ return self.parse_line('Text-content-sha1: ([0-9a-z]+)$', required=False)
-+
-+ def parse_text_length(self):
-+ return self.parse_line('Text-content-length: ([0-9]+)$', required=False)
-+
-+ # One day we may need to parse individual property name/values into a map
-+ def get_props(self):
-+ props = []
-+ while not re.match('PROPS-END$', self.lines[self.current]):
-+ props.append(self.lines[self.current])
-+ self.current += 1
-+ self.current += 1
-+ return props
-+
-+ def get_content(self, length):
-+ content = ''
-+ while len(content) < length:
-+ content += self.lines[self.current]
-+ self.current += 1
-+ if len(content) == length + 1:
-+ content = content[:-1]
-+ elif len(content) != length:
-+ raise SVNDumpParseError("content length expected %d actual %d at line %d"
-+ % (length, len(content), self.current))
-+ return content
-+
-+ def parse_one_node(self):
-+ node = {}
-+ node['kind'] = self.parse_kind()
-+ action = self.parse_action()
-+ node['copyfrom_rev'] = self.parse_copyfrom_rev()
-+ node['copyfrom_path'] = self.parse_copyfrom_path()
-+ node['copy_md5'] = self.parse_copy_md5()
-+ node['copy_sha1'] = self.parse_copy_sha1()
-+ node['prop_length'] = self.parse_prop_length(required=False)
-+ node['text_length'] = self.parse_text_length()
-+ node['text_md5'] = self.parse_text_md5()
-+ node['text_sha1'] = self.parse_text_sha1()
-+ node['content_length'] = self.parse_content_length(required=False)
-+ self.parse_blank()
-+ if node['prop_length']:
-+ node['props'] = self.get_props()
-+ if node['text_length']:
-+ node['content'] = self.get_content(int(node['text_length']))
-+ # Hard to determine how may blanks is 'correct' (a delete that is
-+ # followed by an add that is a replace and a copy has one fewer
-+ # than expected but that can't be predicted until seeing the add)
-+ # so allow arbitrary number
-+ blanks = 0
-+ while self.current < len(self.lines) and self.parse_blank(required=False):
-+ blanks += 1
-+ node['blanks'] = blanks
-+ return action, node
-+
-+ def parse_all_nodes(self):
-+ nodes = {}
-+ while True:
-+ if self.current >= len(self.lines):
-+ break
-+ path = self.parse_path()
-+ if not path and not path is '':
-+ break
-+ if not nodes.get(path):
-+ nodes[path] = {}
-+ action, node = self.parse_one_node()
-+ if nodes[path].get(action):
-+ raise SVNDumpParseError("duplicate action '%s' for node '%s' at line %d"
-+ % (action, path, self.current))
-+ nodes[path][action] = node
-+ return nodes
-+
-+ def parse_one_revision(self):
-+ revision = {}
-+ number = self.parse_revision()
-+ revision['prop_length'] = self.parse_prop_length()
-+ revision['content_length'] = self.parse_content_length()
-+ self.parse_blank()
-+ revision['props'] = self.get_props()
-+ self.parse_blank()
-+ revision['nodes'] = self.parse_all_nodes()
-+ return number, revision
-+
-+ def parse_all_revisions(self):
-+ while self.current < len(self.lines):
-+ number, revision = self.parse_one_revision()
-+ if self.parsed.get(number):
-+ raise SVNDumpParseError("duplicate revision %d at line %d"
-+ % (number, self.current))
-+ self.parsed[number] = revision
-+
-+ def parse(self):
-+ self.parsed['format'] = self.parse_format()
-+ self.parse_blank()
-+ self.parsed['uuid'] = self.parse_uuid()
-+ self.parse_blank()
-+ self.parse_all_revisions()
-+ return self.parsed
-+
-+def compare_dump_files(message, label, expected, actual):
-+ """Parse two dump files EXPECTED and ACTUAL, both of which are lists
-+ of lines as returned by run_and_verify_dump, and check that the same
-+ revisions, nodes, properties, etc. are present in both dumps.
-+ """
-+
-+ parsed_expected = DumpParser(expected).parse()
-+ parsed_actual = DumpParser(actual).parse()
-+
-+ if parsed_expected != parsed_actual:
-+ raise svntest.Failure('\n' + '\n'.join(ndiff(
-+ pprint.pformat(parsed_expected).splitlines(),
-+ pprint.pformat(parsed_actual).splitlines())))
---- subversion-1.7.3/subversion/tests/cmdline/switch_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/switch_tests.py
-@@ -1532,33 +1532,29 @@ def mergeinfo_switch_elision(sbox):
- beta_path = os.path.join(wc_dir, "A", "B", "E", "beta")
-
- # Make branches A/B_COPY_1 and A/B_COPY_2
-- svntest.actions.run_and_verify_svn(
-- None,
-- ["A " + os.path.join(wc_dir, "A", "B_COPY_1", "lambda") + "\n",
-+ expected_stdout = verify.UnorderedOutput([
-+ "A " + os.path.join(wc_dir, "A", "B_COPY_1", "lambda") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_1", "E") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_1", "E", "alpha") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_1", "E", "beta") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_1", "F") + "\n",
- "Checked out revision 1.\n",
-- "A " + B_COPY_1_path + "\n"],
-- [],
-- 'copy',
-- sbox.repo_url + "/A/B",
-- B_COPY_1_path)
--
-- svntest.actions.run_and_verify_svn(
-- None,
-- ["A " + os.path.join(wc_dir, "A", "B_COPY_2", "lambda") + "\n",
-+ "A " + B_COPY_1_path + "\n",
-+ ])
-+ svntest.actions.run_and_verify_svn(None, expected_stdout, [], 'copy',
-+ sbox.repo_url + "/A/B", B_COPY_1_path)
-+
-+ expected_stdout = verify.UnorderedOutput([
-+ "A " + os.path.join(wc_dir, "A", "B_COPY_2", "lambda") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_2", "E") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_2", "E", "alpha") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_2", "E", "beta") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY_2", "F") + "\n",
- "Checked out revision 1.\n",
-- "A " + B_COPY_2_path + "\n"],
-- [],
-- 'copy',
-- sbox.repo_url + "/A/B",
-- B_COPY_2_path)
-+ "A " + B_COPY_2_path + "\n",
-+ ])
-+ svntest.actions.run_and_verify_svn(None, expected_stdout, [], 'copy',
-+ sbox.repo_url + "/A/B", B_COPY_2_path)
-
- expected_output = svntest.wc.State(wc_dir, {
- 'A/B_COPY_1' : Item(verb='Adding'),
---- subversion-1.7.3/subversion/tests/cmdline/update_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/update_tests.py
-@@ -1189,6 +1189,7 @@ def another_hudson_problem(sbox):
-
- # Sigh, I can't get run_and_verify_update to work (but not because
- # of issue 919 as far as I can tell)
-+ expected_output = svntest.verify.UnorderedOutput(expected_output)
- svntest.actions.run_and_verify_svn(None,
- expected_output, [],
- 'up', G_path)
-@@ -3142,19 +3143,17 @@ def mergeinfo_update_elision(sbox):
- lambda_path = os.path.join(wc_dir, "A", "B", "lambda")
-
- # Make a branch A/B_COPY
-- svntest.actions.run_and_verify_svn(
-- None,
-- ["A " + os.path.join(wc_dir, "A", "B_COPY", "lambda") + "\n",
-+ expected_stdout = verify.UnorderedOutput([
-+ "A " + os.path.join(wc_dir, "A", "B_COPY", "lambda") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY", "E") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY", "E", "alpha") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY", "E", "beta") + "\n",
- "A " + os.path.join(wc_dir, "A", "B_COPY", "F") + "\n",
- "Checked out revision 1.\n",
-- "A " + B_COPY_path + "\n"],
-- [],
-- 'copy',
-- sbox.repo_url + "/A/B",
-- B_COPY_path)
-+ "A " + B_COPY_path + "\n",
-+ ])
-+ svntest.actions.run_and_verify_svn(None, expected_stdout, [], 'copy',
-+ sbox.repo_url + "/A/B", B_COPY_path)
-
- expected_output = wc.State(wc_dir, {'A/B_COPY' : Item(verb='Adding')})
-
---- subversion-1.7.3/subversion/tests/cmdline/upgrade_tests.py.hashorder
-+++ subversion-1.7.3/subversion/tests/cmdline/upgrade_tests.py
-@@ -1079,7 +1079,7 @@ def upgrade_with_missing_subdir(sbox):
- svntest.main.safe_rmtree(sbox.ospath('A/B'))
-
- # Now upgrade the working copy and expect a missing subdir
-- expected_output = [
-+ expected_output = svntest.verify.UnorderedOutput([
- "Upgraded '%s'\n" % sbox.wc_dir,
- "Upgraded '%s'\n" % sbox.ospath('A'),
- "Skipped '%s'\n" % sbox.ospath('A/B'),
-@@ -1087,7 +1087,7 @@ def upgrade_with_missing_subdir(sbox):
- "Upgraded '%s'\n" % sbox.ospath('A/D'),
- "Upgraded '%s'\n" % sbox.ospath('A/D/G'),
- "Upgraded '%s'\n" % sbox.ospath('A/D/H'),
-- ]
-+ ])
- svntest.actions.run_and_verify_svn(None, expected_output, [],
- 'upgrade', sbox.wc_dir)
-
diff --git a/subversion-1.7.4-hashorder.patch b/subversion-1.7.4-hashorder.patch
new file mode 100644
index 0000000..2517181
--- /dev/null
+++ b/subversion-1.7.4-hashorder.patch
@@ -0,0 +1,34 @@
+
+Remaining fixes for APR hashorder issues.
+
+--- subversion-1.7.4/subversion/bindings/swig/python/tests/repository.py.hashorder
++++ subversion-1.7.4/subversion/bindings/swig/python/tests/repository.py
+@@ -170,9 +170,11 @@ class SubversionRepositoryTestCase(unitt
+ repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton,
+ _authz_callback, 1, 1, 0, 0)
+
+- # Check results
+- self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n")
+- self.assertEqual(editor.textdeltas[1].new_data, "A test.\n")
++ # Check results.
++ # Ignore the order in which the editor delivers the two sibling files.
++ self.assertEqual(set([editor.textdeltas[0].new_data,
++ editor.textdeltas[1].new_data]),
++ set(["This is a test.\n", "A test.\n"]))
+ self.assertEqual(len(editor.textdeltas), 2)
+
+ def test_retrieve_and_change_rev_prop(self):
+--- subversion-1.7.4/subversion/bindings/swig/python/tests/wc.py.hashorder
++++ subversion-1.7.4/subversion/bindings/swig/python/tests/wc.py
+@@ -216,8 +216,9 @@ class SubversionWorkingCopyTestCase(unit
+
+ def test_entries_read(self):
+ entries = wc.entries_read(self.wc, True)
+-
+- self.assertEqual(['', 'tags', 'branches', 'trunk'], list(entries.keys()))
++ keys = list(entries.keys())
++ keys.sort()
++ self.assertEqual(['', 'branches', 'tags', 'trunk'], keys)
+
+ def test_get_ignores(self):
+ self.assert_(isinstance(wc.get_ignores(None, self.wc), list))
diff --git a/subversion-1.7.4-httpd24.patch b/subversion-1.7.4-httpd24.patch
new file mode 100644
index 0000000..e7fe352
--- /dev/null
+++ b/subversion-1.7.4-httpd24.patch
@@ -0,0 +1,36 @@
+
+http://svn.apache.org/viewvc?view=revision&revision=1232267
+
+--- subversion-1.7.4/subversion/mod_dav_svn/util.c.httpd24
++++ subversion-1.7.4/subversion/mod_dav_svn/util.c
+@@ -624,19 +624,20 @@ void dav_svn__log_err(request_rec *r,
+ /* Log the errors */
+ /* ### should have a directive to log the first or all */
+ for (errscan = err; errscan != NULL; errscan = errscan->prev) {
++ apr_status_t status;
++
+ if (errscan->desc == NULL)
+ continue;
+
+- if (errscan->save_errno != 0) {
+- errno = errscan->save_errno;
+- ap_log_rerror(APLOG_MARK, level, errno, r, "%s [%d, #%d]",
+- errscan->desc, errscan->status, errscan->error_id);
+- }
+- else {
+- ap_log_rerror(APLOG_MARK, level, 0, r,
+- "%s [%d, #%d]",
+- errscan->desc, errscan->status, errscan->error_id);
+- }
++#if AP_MODULE_MAGIC_AT_LEAST(20091119,0)
++ status = errscan->aprerr;
++#else
++ status = errscan->save_errno;
++#endif
++
++ ap_log_rerror(APLOG_MARK, level, status, r,
++ "%s [%d, #%d]",
++ errscan->desc, errscan->status, errscan->error_id);
+ }
+ }
+
diff --git a/subversion.spec b/subversion.spec
index 2d3b051..7b7073c 100644
--- a/subversion.spec
+++ b/subversion.spec
@@ -13,8 +13,8 @@
Summary: A Modern Concurrent Version Control System
Name: subversion
-Version: 1.7.3
-Release: 7%{?dist}
+Version: 1.7.4
+Release: 1%{?dist}
License: ASL 2.0
Group: Development/Tools
URL: http://subversion.apache.org/
@@ -30,7 +30,8 @@ Patch1: subversion-1.7.0-rpath.patch
Patch2: subversion-1.7.0-pie.patch
Patch3: subversion-1.7.0-kwallet.patch
Patch4: subversion-1.7.2-ruby19.patch
-Patch5: subversion-1.7.3-hashorder.patch
+Patch5: subversion-1.7.4-hashorder.patch
+Patch6: subversion-1.7.4-httpd24.patch
BuildRequires: autoconf, libtool, python, python-devel, texinfo, which
BuildRequires: db4-devel >= 4.1.25, swig >= 1.3.24, gettext
BuildRequires: apr-devel >= 1.3.0, apr-util-devel >= 1.3.0
@@ -168,6 +169,7 @@ This package includes supplementary tools for use with Subversion.
%patch3 -p1 -b .kwallet
%patch4 -p1 -b .ruby
%patch5 -p1 -b .hashorder
+%patch6 -p1 -b .httpd24
%build
# Regenerate the buildsystem, so that:
@@ -451,6 +453,10 @@ fi
%endif
%changelog
+* Mon Mar 12 2012 Joe Orton - 1.7.4-1
+- update to 1.7.4
+- fix build with httpd 2.4
+
* Thu Mar 1 2012 Joe Orton - 1.7.3-7
- re-enable kwallet (#791031)