[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Case only rename during merge breaks WC

From: Paul Burba <ptburba_at_gmail.com>
Date: Mon, 3 Mar 2008 20:42:51 -0500

Recently in IRC:

<lisppaste4> markphip pasted "Here is a weird error" at
$ svn st
 M .
M subversion/include/svn_mergeinfo.h
M subversion/libsvn_subr/mergeinfo.c
M subversion/tests/libsvn_subr/mergeinfo-test.c
M subversion/tests/cmdline/log_tests.py
M subversion/libsvn_repos/log.c
$ svn revert -R .
Reverted '.'
Reverted 'subversion/include/svn_mergeinfo.h'
Reverted 'subversion/libsvn_subr/mergeinfo.c'
svn: Error restoring text for

Why is it trying to revert this file that did not show up in status?
<pburba> markphip: Can you reproduce that?
<markphip> pburba: it is with 1.4.6 by the way
<cmpilato> eek.
<markphip> pburba: If I just run svn revert -R . again it happens again.
<markphip> That file was renamed from MergeInfo.java a while back
<markphip> I wonder if that is related?
<cmpilato> can you a) make a copy of the working copy, and b) see if
you can reproduce this while running it in, say,
subversion/bindings/javahl/src/org/tigris/subversion/ ?
<cmpilato> (trying to reduce the data set)
<cmpilato> also, see if 'svn cleanup' make the same complaint.
<durin42> markphip: is tht on OS X?
<markphip> yes
<cmpilato> (revert will try to run outstanding wc logs)
<durin42> markphip: I've seen that before - things don't get listed in
st but are edited. I've never figured out a root cause, it will be
*months* and I'll figure it's fixed, then it bites again. Only fix
I've ever found is a fresh WC.
<markphip> cmpilato, still get it down in the folder tree
<markphip> also, this file does not exist in prop-base or text-base or tmp
<cmpilato> did you try 'svn cleanup' ?
<cmpilato> (on your copy of the wc)
<markphip> yes
<cmpilato> and?
<markphip> nothing
<cmpilato> no error? no fix of the problem?
<markphip> this could be an issue because OSX is not a true case
sensitive file system
<cmpilato> could be.
<pburba> markphip, cmpilato: I can reproduce with trunk on Win32:
<markphip> it must be the handling of a rename on case insensitive fs
<pburba> markphip: I'll add a test then look into it
<pburba> Actually, Ill make sure we dont already have a test/issue...
<markphip> pburba: it should be easy to do the same test with update
instead of merge
<markphip> that would give some clues if it did not have a problem
<pburba> yup
<markphip> I notice in merge the A came before the D
<markphip> I thought that was the way we fixed it in update ... D before A
<pburba> markphip: I spoke too soon, how are you figuring to use an
update in this situation?
<markphip> can't you just checkout the revision before this change and
then run update?
<pburba> Sure, but there won't be any local mods to revert.
<markphip> that is not related to the problem
<pburba> The Delete preceeds the Add with the update if that is all
you are wondering
<markphip> the changes I was reverting were completely different
<markphip> I had merged and committed this change a long time ago
<pburba> Ok, you didnt have changes to the renamed file before the
revert, so my example is not the same.
<markphip> I think the merge created the problem
<markphip> I just did not see it until later when I was reverting some stuff
<pburba> markphip: A simpler example of the problem:
<markphip> pburba: yes that is what I did
<pburba> markphip: Nothing similar when using update (is this what you meant?)
<pburba> http://rifers.org/paste/show/6724
<markphip> yes, exactly

Well it's probably no surprise given the number of case-insensitivity
related issues in the tracker, but it looks like we have another: On
platforms with case insensitive file systems, when performing a merge
which moves a path within the same parent directory, and when the
source and destination of the move differ *only* by case, then the WC
can end up broken. In the case of merging a file move, the merge
succeeds and we can commit the merge, but a cryptic error occurs on
subsequent revert attempts of the WC. Here is a really simple case:

(All examples which follow start with a standard greek tree from the
test suite when r2 is a copy of 'A' to 'A_COPY'):

>svn st merge_tests-87 -v
                1 1 jrandom merge_tests-87
                1 1 jrandom merge_tests-87\A
                1 1 jrandom merge_tests-87\A\B
                1 1 jrandom merge_tests-87\A\B\lambda
                1 1 jrandom merge_tests-87\A\B\E
                1 1 jrandom merge_tests-87\A\B\E\alpha
                1 1 jrandom merge_tests-87\A\B\E\beta
                1 1 jrandom merge_tests-87\A\B\F
                1 1 jrandom merge_tests-87\A\mu
                1 1 jrandom merge_tests-87\A\C
                1 1 jrandom merge_tests-87\A\D
                1 1 jrandom merge_tests-87\A\D\gamma
                1 1 jrandom merge_tests-87\A\D\G
                1 1 jrandom merge_tests-87\A\D\G\pi
                1 1 jrandom merge_tests-87\A\D\G\rho
                1 1 jrandom merge_tests-87\A\D\G\tau
                1 1 jrandom merge_tests-87\A\D\H
                1 1 jrandom merge_tests-87\A\D\H\chi
                1 1 jrandom merge_tests-87\A\D\H\omega
                1 1 jrandom merge_tests-87\A\D\H\psi
                2 2 jrandom merge_tests-87\A_COPY
                2 2 jrandom merge_tests-87\A_COPY\B
                2 2 jrandom merge_tests-87\A_COPY\B\lambda
                2 2 jrandom merge_tests-87\A_COPY\B\E
                2 2 jrandom merge_tests-87\A_COPY\B\E\alpha
                2 2 jrandom merge_tests-87\A_COPY\B\E\beta
                2 2 jrandom merge_tests-87\A_COPY\B\F
                2 2 jrandom merge_tests-87\A_COPY\mu
                2 2 jrandom merge_tests-87\A_COPY\C
                2 2 jrandom merge_tests-87\A_COPY\D
                2 2 jrandom merge_tests-87\A_COPY\D\gamma
                2 2 jrandom merge_tests-87\A_COPY\D\G
                2 2 jrandom merge_tests-87\A_COPY\D\G\pi
                2 2 jrandom merge_tests-87\A_COPY\D\G\rho
                2 2 jrandom merge_tests-87\A_COPY\D\G\tau
                2 2 jrandom merge_tests-87\A_COPY\D\H
                2 2 jrandom merge_tests-87\A_COPY\D\H\chi
                2 2 jrandom merge_tests-87\A_COPY\D\H\omega
                2 2 jrandom merge_tests-87\A_COPY\D\H\psi
                1 1 jrandom merge_tests-87\iota

>svn move %url%/A/mu %url%/A/MU -m "case only file move"

Committed revision 3.

>svn merge %url%/A merge_tests-87\A_COPY -c3
--- Merging r3 into 'merge_tests-87\A_COPY':
A merge_tests-87\A_COPY\MU
D merge_tests-87\A_COPY\mu

# Can't have the text bases for both 'mu' and 'MU' on a case
# insensitive file system. This is the start of our troubles.
>dir merge_tests-87\A_COPY\.svn\text-base
 Volume in drive C is Local Disk
 Volume Serial Number is FCC3-CBDB

 Directory of merge_tests-87\A_COPY\.svn\text-base

03/03/2008 02:52 PM <DIR> .
03/03/2008 02:52 PM <DIR> ..
03/03/2008 02:52 PM 23 MU.svn-base
               1 File(s) 23 bytes
               2 Dir(s) 26,149,707,776 bytes free

# We can commit the above change, but...
>svn ci -m "" merge_tests-87
Sending merge_tests-87\A_COPY
Adding merge_tests-87\A_COPY\MU
Deleting merge_tests-87\A_COPY\mu

Committed revision 4.

# ...svn_client_commit4() when running the log deletes the 'MU' text base
# when it tries to delete the 'mu' text base and without a text base for 'MU'...
>dir merge_tests-87\A_COPY\.svn\text-base
 Volume in drive C is Local Disk
 Volume Serial Number is FCC3-CBDB

 Directory of merge_tests-87\A_COPY\.svn\text-base

03/03/2008 02:53 PM <DIR> .
03/03/2008 02:53 PM <DIR> ..
               0 File(s) 0 bytes
               2 Dir(s) 26,149,707,776 bytes free

# ...revert is forever broken on this WC:
>svn revert -R merge_tests-87
..\..\..\subversion\libsvn_wc\adm_ops.c:1823: (apr_err=2)
svn: Error restoring text for 'merge_tests-87\A_COPY\MU'

The only solutions are to checkout new version of the working copy or
delete 'A_COPY' via the OS and then update the WC. An update alone
isn't sufficient since update doesn't restore missing text bases.

Obviously this isn't a huge problem as a lot of things have to go just
right (wrong?) for the problem to occur. Notably, unlike merge,
update doesn't need to be "revertable" so the deletion can take place
first and then the add (see r12616) so only the user doing the merge
is impacted. Regardless, it would be nice to fix. I see two possible
solutions for the file move case:

1) Make update restore missing text bases. This doesn't fix the
problem, but in combination with a tweak to the error message
suggesting an update, it would be easier and more obvious on how to
fix it.

2) Make svn_wc_remove_from_revision_control()'s helper
remove_file_if_present() stat every file it tries to remove and to
*not* remove the file if the case of the requested file doesn't match
what's on disk, something like this:

 /* Remove FILE if it exists and is a file. If it does not exist, do
    nothing. If it is not a file, error. */
 static svn_error_t *
 remove_file_if_present(const char *file, apr_pool_t *pool)
   svn_error_t *err;
+#if defined(WIN32) || defined(DARWIN)
+ /* On platforms with case-insensitive file systems, make sure that FILE
+ and the name of the file on disk agree on case before we delete. */
+ apr_finfo_t finfo;

+ err = svn_io_stat(&finfo, file, APR_FINFO_NAME | APR_FINFO_ICASE, pool);
+ if (err
+ && !(err->apr_err == APR_INCOMPLETE
+ || APR_STATUS_IS_ENOENT(err->apr_err)))
+ return err;
+ svn_error_clear(err);
+ if ((APR_FINFO_NAME | finfo.valid)
+ && svn_path_compare_paths(svn_path_basename(file, pool), finfo.name))
+ return SVN_NO_ERROR;
   /* Try to remove the file. */
   err = svn_io_remove_file(file, pool);

   /* Ignore file not found error. */
   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
       err = SVN_NO_ERROR;

   return err;

Any thoughts on 1) or 2) or ideas for other alternatives?

Note that merging a directory move doesn't work at all:

# Do a case-only move of a directory within the same parent:
>svn move %url%/A/D %url%/A/d -m ""

Committed revision 3.

# Merge that move into 'A_COPY':
>svn merge %url%/A merge_tests-87\A_COPY -c3
..\..\..\subversion\libsvn_wc\lock.c:366: (apr_err=155004)
svn: Working copy 'merge_tests-87\A_COPY\d' locked
svn: run 'svn cleanup' to remove locks (type 'svn help cleanup' for details)

The problem is that the whole tree rooted at 'A_COPY' gets locked at
the start of the merge. Then the svn_wc_diff_callbacks2_t callback
merge_dir_added() doesn't find 'A_COPY/d' in the admin access (since
that *is* case sensitive) and not finding it attempts to open a new
access, but in doing so runs into the existing 'A_COPY/D'. I haven't
thought much on how to solve this; I want to solve the simpler file
case first.


To unsubscribe, e-mail: dev-unsubscribe_at_subversion.tigris.org
For additional commands, e-mail: dev-help_at_subversion.tigris.org
Received on 2008-03-04 02:43:13 CET

This is an archived mail posted to the Subversion Dev mailing list.