Bert Huijben <[email protected]> writes:
> Noting everything as a change even if there is no actual delta... still
> looks like a bug we should fix (and we did) instead of something that we
> should restore.
>
> For the reasoning on why:
>
> For blame we are really interested if there are deltas... and if there
> aren't any changes the revision is not 'interesting'. (See docs of the api).
The place where we define "interesting" revisions is svn_fs_history_prev2():
/** Set @a *prev_history_p to an opaque node history object which
* represents the previous (or "next oldest") interesting history
* location for the filesystem node represented by @a history, or @c
* NULL if no such previous history exists. If @a cross_copies is @c
* FALSE, also return @c NULL if stepping backwards in history to @a
* *prev_history_p would cross a filesystem copy operation.
*
* @note If this is the first call to svn_fs_history_prev() for the @a
* history object, it could return a history object whose location is
* the same as the original. This will happen if the original
* location was an interesting one (where the node was modified, or
* took place in a copy event). This behavior allows looping callers
* to avoid the calling svn_fs_history_location() on the object
* returned by svn_fs_node_history(), and instead go ahead and begin
* calling svn_fs_history_prev().
...
Functions like svn_ra_get_file_revs2() that you mentioned just link to it:
/**
* Retrieve a subset of the interesting revisions of a file @a path
* as seen in revision @a end (see svn_fs_history_prev() for a
* definition of "interesting revisions").
...
The svn_fs_history_prev2() function reports revisions with empty deltas as
interesting, and has been doing that for years (see the attached patch with
a test). This behavior did not change in 1.9.
This means that after r1572363 and r1573111, svn_ra_get_file_revs2() and
svn_repos_get_file_revs2() were skipping some of the "interesting" revisions,
according to the FS API defining the concept. Moreover, this behavior could
be inconsistent even within a single function like svn_ra_get_file_revs2()
that calls svn_ra_get_log2() for old servers, as get-log notices revisions
with empty deltas.
I think that it's another example of where r1572363 and r1573111 introduce an
inconsistent and unwanted behavior change.
Stefan Fuhrmann <[email protected]> writes:
>> As for /trunk, I think that we could do that, so I sketched this option in
>> the attached patch.
>
> The patch looks o.k.
Thanks for giving this patch a look.
I examined the differences between these functions, svn_repos__compare_files()
and svn_fs_contents_different(), and I think that we also have a problem in
FSFS's implementation of the new svn_fs_contents_different() API in 1.9.x.
The implementation relies on representation_t.expanded_size value when doing
the comparison. If this value is zero, a representation is considered empty,
and two empty representations are considered equal.
The problem is that the expanded_size can be zero for empty files and also for
some of the PLAIN representations, which are not empty. If I am not mistaken,
we have a workaround for this in /trunk — r1673875 [1] and follow-ups, but not
in Subversion 1.9. Hence, the new svn_fs_contents_different() API can fail to
distinguish different contents in 1.9.x under the circumstances described in
issue 4554 [2].
So, we can use it in trunk, but this API may produce invalid results in 1.9.x.
I'll commit the patch, and then I am going to prepare the nomination consisting
of r1709388 and the new dump_no_op_change() test.
[1] https://svn.apache.org/r1673875
[2] https://issues.apache.org/jira/browse/SVN-4554
Regards,
Evgeny Kotkov
Index: subversion/tests/libsvn_fs/fs-test.c
===================================================================
--- subversion/tests/libsvn_fs/fs-test.c (revision 1710620)
+++ subversion/tests/libsvn_fs/fs-test.c (working copy)
@@ -6971,6 +6971,47 @@ freeze_and_commit(const svn_test_opts_t *opts,
return SVN_NO_ERROR;
}
+static svn_error_t *
+history_with_empty_delta(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_revnum_t head_rev = 0;
+ svn_fs_root_t *root;
+ svn_fs_txn_t *txn;
+ svn_fs_history_t *history;
+ const char *path;
+ svn_revnum_t revision;
+
+ SVN_ERR(svn_test__create_fs(&fs, "test-history-with-empty-delta",
+ opts, pool));
+
+ /* r1: Create the greek tree. */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+ SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+ SVN_ERR(svn_test__create_greek_tree(root, pool));
+ SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+ /* r2: Commit an empty delta for /iota. */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+ SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+ SVN_ERR(svn_test__set_file_contents(root, "/iota",
+ "This is the file 'iota'.\n", pool));
+ SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+ /* Get the first real point of interesting history. */
+ SVN_ERR(svn_fs_revision_root(&root, fs, head_rev, pool));
+ SVN_ERR(svn_fs_node_history2(&history, root, "/iota", pool, pool));
+ SVN_ERR(svn_fs_history_prev2(&history, history, FALSE, pool, pool));
+
+ /* The revision with empty delta is reported as "interesting". */
+ SVN_ERR(svn_fs_history_location(&path, &revision, history, pool));
+ SVN_TEST_STRING_ASSERT(path, "/iota");
+ SVN_TEST_ASSERT(revision == 2);
+
+ return SVN_NO_ERROR;
+}
+
/* ------------------------------------------------------------------------ */
/* The test table. */
@@ -7105,6 +7146,8 @@ static struct svn_test_descriptor_t test_funcs[] =
"test svn_fs_check_related for transactions"),
SVN_TEST_OPTS_PASS(freeze_and_commit,
"freeze and commit"),
+ SVN_TEST_OPTS_PASS(history_with_empty_delta,
+ "test node history with empty delta"),
SVN_TEST_NULL
};