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

Re: Error when creating tags from working copy

From: Karl Fogel <kfogel_at_red-bean.com>
Date: Sat, 11 Oct 2008 18:25:24 -0400

This is a deep one. I'm posting what I've learned so far in case anyone
has any hunches. On the client, the problem is that in

   libsvn_ra_neon/commit.c:commit_add_file()

the following code should get a "file not found" error (i.e., an HTTP
404) when it probes for the existence of the file we're about to add,
but doesn't -- instead, it's as though the file already exists:

      svn_ra_neon__resource_t *res;
      svn_error_t *err = svn_ra_neon__get_starting_props(&res,
                                                         file->cc->ras,
                                                         file->rsrc->wr_url,
                                                         NULL, workpool);
      if (!err)
        {
          /* If the PROPFIND succeeds the file already exists */
          return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL,
                                   _("File '%s' already exists"),
                                   file->rsrc->url);
        }

Here's what happens on the server side. Let's shortcut directly to
mod_dav_svn/repos.c:get_resource(). Before the call to
prep_resource()...

  /* prepare the resource for operation */
  if ((err = prep_resource(comb)) != NULL)
    return err;

...comb->exists is 0, as we would want (so is comb->res.exists, for that
matter). But after prep_resource(), comb->exists is 1!

prep_resource() is just a dispatch function; the real stuff happens in
prep_working() (because comb->res.type == DAV_RESOURCE_TYPE_WORKING).
In prep_working(), we get all the way to this code near the end:

  derr = fs_check_path(&kind, comb->priv.root.root,
                       comb->priv.repos_path, pool);

  if (derr != NULL)
    return derr;

  comb->res.exists = (kind == svn_node_none) ? FALSE : TRUE;
  comb->res.collection = (kind == svn_node_dir) ? TRUE : FALSE;

So that's where comb->res.exists gets set to 1. (I'm not sure where
comb->exists gets set to 1, which might be important, but see below.)

Anyway, that call to fs_check_path() sets kind to svn_node_file, even
though the file shouldn't be present in this txn yet (that's why we're
adding it).

Tracing fs_check_path --> svn_fs_fs__check_path() --> node_kind() -->
fs_node_id() --> get_dag() --> open_path() --> svn_fs_fs__dag_open(),
shows that, somehow, the file is actually there, according to the FS.
(It does not appear to be a dag node caching problem; the node isn't
found in the cache, instead we find it the old-fashioned way, right
there in the filesystem.)

Back up in http-2.2.9/modules/dav/main/mod_dav.c:dav_method_propfind(),
this all matters because of this code:

    /* Ask repository module to resolve the resource */
    err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
                           &resource);
    if (err != NULL)
        return dav_handle_err(r, err, NULL);

    if (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) {
        /* Apache will supply a default error for this. */
        return HTTP_NOT_FOUND;
    }

Not only does dav_get_resource() not get an error, but the call to
dav_get_resource_state() will not return DAV_RESOURCE_NULL, because that
code (in httpd-2.2.9/modules/dav/main/util_lock.c) says:

    if (resource->exists)
        return DAV_RESOURCE_EXISTS;

Boom. If it weren't for that, the function would return
DAV_RESOURCE_NULL and we'd be fine.

Note that in the reproduction recipe, if you insert an "svn up Data/wc"
right before the final svn cp in the reproduction recipe, then none of
this weirdness happens, and the bug doesn't reproduce.

So why does the file claim to be present in the txn already? And why
does updating the whole working copy to r2 (instead of having a mixed
r1/r2 working copy) cause the repository filesystem to correctly answer
that the file is not there?

I don't know. I will continue to debug, but if anyone has any inspired
ideas, please post.

-Karl

Karl Fogel <kfogel_at_red-bean.com> writes:
> "Michael Susser" <hdmlist_at_googlemail.com> writes:
>> i have found a bug when creating tags from working copy in svn 1.5.2
>> (and also 1.5.3). Here is a recipe how to reproduce the error: (my
>> apache is configured to host the repositories in E:\SVNRep)
>>
>> [...]
>>
>> svn copy -m"tagging" E:\Data\wc\trunk http://myserver/svn/repo/tags/V1.0.0
>>>svn: Commit failed (details follow):
>>>svn: File '/svn/repo/tags/V1.0.0/dir/file.cpp' already exists
>
> Thank you for the excellent recipe. I can reproduce this, and have
> written a Unix Bourne shell reproduction script (see below). Note that
> this only reproduces over http://. If you try it with svn:// or
> file://, the tagging works fine.
>
>> This error message is absolutely wrong! I investigated what went wrong and
>> found the following: after the commit action the working copy is not updated
>> correctly(?). The changed file "file.cpp" gets a new revision (2), but its
>> parent directory "dir" stays at revision 1. Is this ok?
>
> Well, that part is okay. When you commit, only the committed items get
> their revisions bumped locally -- in other words, a commit does not do
> an update on the items that are not part of the commit. However,
> Subversion is supposed to transparently handle the resultant mixed
> revisions. They shouldn't matter here.
>
> This bug seems familiar, like we've either fixed it before or filed an
> issue for it. I'll check the logs and the issue tracker. But we have a
> reproduction recipe, so that means we can fix it or re-fix it :-).
>
> ----------------------------------------------------------------------------
> #!/bin/sh
>
> # The next line is the only line you should need to adjust.
> SVNDIR=/home/kfogel/src/subversion
>
> SVN=${SVNDIR}/subversion/svn/svn
> SVNSERVE=${SVNDIR}/subversion/svnserve/svnserve
> SVNADMIN=${SVNDIR}/subversion/svnadmin/svnadmin
>
> # Select an access method. If svn://, the svnserve setup is
> # handled automagically by this script; but if http://, then
> # you'll have to configure it yourself first.
> #
> URL=http://localhost/ms/repos
> # URL=svn://localhost/repos
> # URL=file:///`pwd`/repos
>
> rm -rf repos wc Data import-me
>
> ${SVNADMIN} create repos
>
> # These are for svnserve only.
> echo "[general]" > repos/conf/svnserve.conf
> echo "anon-access = write" >> repos/conf/svnserve.conf
> echo "auth-access = write" >> repos/conf/svnserve.conf
>
> # The server will only be contacted if $URL is svn://foo, of course.
> ${SVNSERVE} --pid-file svnserve-pid -d -r `pwd`
> # And put the kill command in a file, in case need to run it manually.
> echo "kill -9 `cat svnserve-pid`" > k
> chmod a+rwx k
>
> mkdir Data
> svn co ${URL} Data/wc
> mkdir Data/wc/tags
> mkdir Data/wc/trunk
> mkdir Data/wc/trunk/dir
> cp /dev/null Data/wc/trunk/dir/file.cpp
> ${SVN} add Data/wc/t*
> ${SVN} ci -m"test" Data/wc
>
> echo "ABC" >> Data/wc/trunk/dir/file.cpp
>
> ${SVN} ci -m"test" Data/wc
>
> # Using http:// only, the next command fails with this error:
> #
> # subversion/libsvn_client/copy.c:1319: (apr_err=175005)
> # svn: Commit failed (details follow):
> # subversion/libsvn_ra_neon/commit.c:1036: (apr_err=175005)
> # svn: File '/ms/repos/tags/V1.0.0/dir/file.cpp' already exists
> #
> ${SVN} copy -m"tagging" Data/wc/trunk ${URL}/tags/V1.0.0
>
> ./k

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe_at_subversion.tigris.org
For additional commands, e-mail: users-help_at_subversion.tigris.org
Received on 2008-10-12 00:25:46 CEST

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

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.