Skip to content

Commit 1f52ec4

Browse files
committed
Use bytewise content comparison in the "is the file modified?" working copy
checks if we have the pristine file content in the working copy. The revised approach prefers bytewise comparison to the checksum comparison if the pristine content is available. This also restores the 1.14.x behavior for existing working copies with pristines or new working copies created with the --store-pristine=yes option, and avoids a potential change in characteristics for file comparisons in them. Discussed in [1]. * subversion/libsvn_wc/questions.c (compare_exact): New helper function, factored out from compare_and_verify() and extended to support both bytewise and checksum-based comparisons. (compare_and_verify): Accept `pristine_stream` and `pristine_size` from the caller. Perform bytewise or checksum-based comparison depending on whether `pristine_stream` is available. (svn_wc__internal_file_modified_p): Call svn_wc__db_pristine_read(). Adjust the call to compare_and_verify(). Handle EACCES to SVN_ERR_WC_PATH_ACCESS_DENIED error conversion at this level. [1]: https://lists.apache.org/thread/128rgf7ozn72ljfzn7o18gfhb9l43y26 git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1931549 13f79535-47bb-0310-9956-ffa450edef68
1 parent d62023d commit 1f52ec4

File tree

1 file changed

+149
-96
lines changed

1 file changed

+149
-96
lines changed

subversion/libsvn_wc/questions.c

Lines changed: 149 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,99 @@
7777
mark it for removal?
7878
*/
7979

80+
static svn_error_t *
81+
compare_exact(svn_boolean_t *modified_p,
82+
svn_stream_t *stream,
83+
const char *eol_str,
84+
apr_hash_t *keywords,
85+
const svn_checksum_t *pristine_checksum,
86+
svn_stream_t *pristine_stream,
87+
const char *pristine_eol_str,
88+
apr_pool_t *scratch_pool)
89+
{
90+
svn_boolean_t same;
91+
92+
/* For exact comparison, we check that contents remain the same when
93+
retranslated according to file properties. */
94+
95+
if (pristine_stream)
96+
{
97+
/* We have pristine contents: wrap a stream to translate it into working
98+
copy form, and check that contents remain the same. */
99+
100+
pristine_stream = svn_subst_stream_translated(pristine_stream,
101+
eol_str, FALSE,
102+
keywords, TRUE,
103+
scratch_pool);
104+
105+
SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, stream,
106+
scratch_pool));
107+
}
108+
else
109+
{
110+
svn_checksum_t *working_checksum;
111+
svn_checksum_t *detranslated_checksum;
112+
svn_checksum_t *retranslated_checksum;
113+
114+
/* We don't have pristine contents. To make the comparison work without
115+
it, let's check for two things:
116+
117+
1) That the checksum of the detranslated contents matches the recorded
118+
pristine checksum, as in the case of a non-exact comparison, ...
119+
120+
2) ...and additionally, that the contents of the working file does not
121+
change when retranslated according to its properties.
122+
123+
Technically we're going to do that with a single read of the file
124+
contents, while checksumming it's original, detranslated and
125+
retranslated versions.
126+
*/
127+
128+
stream = svn_stream_checksummed2(stream,
129+
&working_checksum, NULL,
130+
pristine_checksum->kind, TRUE,
131+
scratch_pool);
132+
133+
stream = svn_subst_stream_translated(stream,
134+
pristine_eol_str, TRUE,
135+
keywords, FALSE,
136+
scratch_pool);
137+
stream = svn_stream_checksummed2(stream,
138+
&detranslated_checksum, NULL,
139+
pristine_checksum->kind, TRUE,
140+
scratch_pool);
141+
142+
stream = svn_subst_stream_translated(stream, eol_str, FALSE,
143+
keywords, TRUE,
144+
scratch_pool);
145+
stream = svn_stream_checksummed2(stream,
146+
&retranslated_checksum, NULL,
147+
pristine_checksum->kind, TRUE,
148+
scratch_pool);
149+
150+
SVN_ERR(svn_stream_copy3(stream, svn_stream_empty(scratch_pool),
151+
NULL, NULL, scratch_pool));
152+
153+
same = svn_checksum_match(detranslated_checksum, pristine_checksum) &&
154+
svn_checksum_match(working_checksum, retranslated_checksum);
155+
}
156+
157+
*modified_p = !same;
158+
return SVN_NO_ERROR;
159+
}
80160

81161
/* Set *MODIFIED_P to TRUE if (after translation) VERSIONED_FILE_ABSPATH
82-
* (of VERSIONED_FILE_SIZE bytes) differs from pristine file with checksum
83-
* PRISTINE_CHECKSUM, else to FALSE if not.
162+
* (of VERSIONED_FILE_SIZE bytes) differs from PRISTINE_STREAM (of
163+
* PRISTINE_SIZE bytes with PRISTINE_CHECKSUM), else to FALSE if not.
164+
*
165+
* If PRISTINE_STREAM is NULL, perform checksum-based content comparison.
166+
* If PRISTINE_STREAM is not NULL, perform bytewise content comparison
167+
* against the provided stream. PRISTINE_STREAM will be closed before
168+
* a successful return.
84169
*
85170
* If EXACT_COMPARISON is FALSE, translate VERSIONED_FILE_ABSPATH's EOL
86171
* style and keywords to repository-normal form according to its properties,
87-
* calculate checksum and compare the result with PRISTINE_CHECKSUM.
172+
* and compare the result with pristine contents.
88173
* If EXACT_COMPARISON is TRUE, also check that VERSIONED_FILE_ABSPATH
89174
* contents remains the same when retranslated according to its properties.
90175
*
@@ -94,13 +179,16 @@
94179
* PROPS_MOD should be TRUE if the file's properties have been changed,
95180
* otherwise FALSE.
96181
*
182+
*
97183
* DB is a wc_db; use SCRATCH_POOL for temporary allocation.
98184
*/
99185
static svn_error_t *
100186
compare_and_verify(svn_boolean_t *modified_p,
101187
svn_wc__db_t *db,
102188
const char *versioned_file_abspath,
103189
svn_filesize_t versioned_file_size,
190+
svn_stream_t *pristine_stream,
191+
svn_filesize_t pristine_size,
104192
const svn_checksum_t *pristine_checksum,
105193
svn_boolean_t has_props,
106194
svn_boolean_t props_mod,
@@ -113,8 +201,7 @@ compare_and_verify(svn_boolean_t *modified_p,
113201
svn_boolean_t special = FALSE;
114202
svn_boolean_t need_translation;
115203
svn_stream_t *v_stream; /* versioned_file */
116-
svn_checksum_t *v_checksum;
117-
svn_error_t *err;
204+
svn_boolean_t same;
118205

119206
SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath));
120207

@@ -140,20 +227,15 @@ compare_and_verify(svn_boolean_t *modified_p,
140227
else
141228
need_translation = FALSE;
142229

143-
if (! need_translation)
230+
/* Easy out check: different sizes with no translation mean the
231+
* file was modified. */
232+
if (!need_translation && versioned_file_size != pristine_size)
144233
{
145-
svn_filesize_t pristine_size;
234+
if (pristine_stream)
235+
SVN_ERR(svn_stream_close(pristine_stream));
146236

147-
SVN_ERR(svn_wc__db_pristine_read(NULL, &pristine_size, db,
148-
versioned_file_abspath, pristine_checksum,
149-
scratch_pool, scratch_pool));
150-
151-
if (versioned_file_size != pristine_size)
152-
{
153-
*modified_p = TRUE;
154-
155-
return SVN_NO_ERROR;
156-
}
237+
*modified_p = TRUE;
238+
return SVN_NO_ERROR;
157239
}
158240

159241
/* ### Other checks possible? */
@@ -169,13 +251,8 @@ compare_and_verify(svn_boolean_t *modified_p,
169251
/* We don't use APR-level buffering because the comparison function
170252
* will do its own buffering. */
171253
apr_file_t *file;
172-
err = svn_io_file_open(&file, versioned_file_abspath, APR_READ,
173-
APR_OS_DEFAULT, scratch_pool);
174-
/* Convert EACCESS on working copy path to WC specific error code. */
175-
if (err && APR_STATUS_IS_EACCES(err->apr_err))
176-
return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
177-
else
178-
SVN_ERR(err);
254+
SVN_ERR(svn_io_file_open(&file, versioned_file_abspath, APR_READ,
255+
APR_OS_DEFAULT, scratch_pool));
179256
v_stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
180257

181258
if (need_translation)
@@ -188,79 +265,41 @@ compare_and_verify(svn_boolean_t *modified_p,
188265
pristine_eol_str = eol_str;
189266

190267
if (exact_comparison)
191-
{
192-
svn_checksum_t *working_checksum;
193-
svn_checksum_t *detranslated_checksum;
194-
svn_checksum_t *retranslated_checksum;
195-
196-
v_stream = svn_stream_checksummed2(v_stream,
197-
&working_checksum, NULL,
198-
pristine_checksum->kind, TRUE,
199-
scratch_pool);
200-
201-
v_stream = svn_subst_stream_translated(v_stream,
202-
pristine_eol_str, TRUE,
203-
keywords, FALSE,
204-
scratch_pool);
205-
v_stream = svn_stream_checksummed2(v_stream,
206-
&detranslated_checksum, NULL,
207-
pristine_checksum->kind, TRUE,
208-
scratch_pool);
209-
210-
v_stream = svn_subst_stream_translated(v_stream, eol_str, FALSE,
211-
keywords, TRUE,
212-
scratch_pool);
213-
v_stream = svn_stream_checksummed2(v_stream,
214-
&retranslated_checksum, NULL,
215-
pristine_checksum->kind, TRUE,
268+
return svn_error_trace(compare_exact(modified_p,
269+
v_stream, eol_str, keywords,
270+
pristine_checksum,
271+
pristine_stream,
272+
pristine_eol_str,
273+
scratch_pool));
274+
275+
/* Wrap file stream to detranslate into normal form,
276+
* "repairing" the EOL style if it is inconsistent. */
277+
v_stream = svn_subst_stream_translated(v_stream,
278+
pristine_eol_str,
279+
TRUE /* repair */,
280+
keywords,
281+
FALSE /* expand */,
216282
scratch_pool);
217-
218-
err = svn_stream_copy3(v_stream, svn_stream_empty(scratch_pool),
219-
NULL, NULL, scratch_pool);
220-
/* Convert EACCESS on working copy path to WC specific error code. */
221-
if (err && APR_STATUS_IS_EACCES(err->apr_err))
222-
return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
223-
else
224-
SVN_ERR(err);
225-
226-
if (svn_checksum_match(detranslated_checksum, pristine_checksum) &&
227-
svn_checksum_match(working_checksum, retranslated_checksum))
228-
{
229-
*modified_p = FALSE;
230-
}
231-
else
232-
{
233-
*modified_p = TRUE;
234-
}
235-
236-
return SVN_NO_ERROR;
237-
}
238-
else
239-
{
240-
/* Wrap file stream to detranslate into normal form,
241-
* "repairing" the EOL style if it is inconsistent. */
242-
v_stream = svn_subst_stream_translated(v_stream,
243-
pristine_eol_str,
244-
TRUE /* repair */,
245-
keywords,
246-
FALSE /* expand */,
247-
scratch_pool);
248-
}
249283
}
250284
}
251285

252-
/* Get checksum of detranslated (normalized) content. */
253-
err = svn_stream_contents_checksum(&v_checksum, v_stream,
254-
pristine_checksum->kind,
255-
scratch_pool, scratch_pool);
256-
/* Convert EACCESS on working copy path to WC specific error code. */
257-
if (err && APR_STATUS_IS_EACCES(err->apr_err))
258-
return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
286+
if (pristine_stream)
287+
{
288+
SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
289+
scratch_pool));
290+
}
259291
else
260-
SVN_ERR(err);
292+
{
293+
svn_checksum_t *v_checksum;
261294

262-
*modified_p = (! svn_checksum_match(v_checksum, pristine_checksum));
295+
SVN_ERR(svn_stream_contents_checksum(&v_checksum, v_stream,
296+
pristine_checksum->kind,
297+
scratch_pool,
298+
scratch_pool));
299+
same = svn_checksum_match(v_checksum, pristine_checksum);
300+
}
263301

302+
*modified_p = !same;
264303
return SVN_NO_ERROR;
265304
}
266305

@@ -279,6 +318,9 @@ svn_wc__internal_file_modified_p(svn_boolean_t *modified_p,
279318
svn_boolean_t has_props;
280319
svn_boolean_t props_mod;
281320
const svn_io_dirent2_t *dirent;
321+
svn_stream_t *pristine_stream;
322+
svn_filesize_t pristine_size;
323+
svn_error_t *err;
282324

283325
/* Read the relevant info */
284326
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
@@ -358,12 +400,23 @@ svn_wc__internal_file_modified_p(svn_boolean_t *modified_p,
358400
}
359401

360402
compare_them:
361-
/* Check all bytes, and verify checksum if requested. */
362-
SVN_ERR(compare_and_verify(modified_p, db,
363-
local_abspath, dirent->filesize,
364-
checksum, has_props, props_mod,
365-
exact_comparison,
366-
scratch_pool));
403+
SVN_ERR(svn_wc__db_pristine_read(&pristine_stream, &pristine_size,
404+
db, local_abspath, checksum,
405+
scratch_pool, scratch_pool));
406+
407+
err = compare_and_verify(modified_p, db,
408+
local_abspath, dirent->filesize,
409+
pristine_stream, pristine_size,
410+
checksum, has_props, props_mod,
411+
exact_comparison,
412+
scratch_pool);
413+
414+
/* At this point we already opened the pristine file, so we know that
415+
the access denied applies to the working copy path. */
416+
if (err && APR_STATUS_IS_EACCES(err->apr_err))
417+
return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
418+
else
419+
SVN_ERR(err);
367420

368421
if (!*modified_p)
369422
{

0 commit comments

Comments
 (0)