Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot compute churn metrics for SVN XML logs #6

Open
igilham opened this issue Feb 17, 2015 · 7 comments
Open

Cannot compute churn metrics for SVN XML logs #6

igilham opened this issue Feb 17, 2015 · 7 comments

Comments

@igilham
Copy link

igilham commented Feb 17, 2015

Using the SVN log format mentioned in the README file for code-maat:

svn log -v --xml > logfile.log -r {YYYYmmDD}:HEAD

I have encountered the following error:

$ maat -l ~/workspace/project/logfile.log -c svn -a abs-churn
java.lang.IllegalArgumentException: Internal error - please report it. Details = churn analysis: the given VCS data doesn't contain modification metrics. Check the code-maat docs for supported VCS and correct log format.
    at code_maat.app.app$throw_internal_error.invoke(app.clj:158)
    at code_maat.app.app$run_with_recovery_point.invoke(app.clj:167)
    at code_maat.app.app$run.invoke(app.clj:188)
    at code_maat.cmd_line$_main.doInvoke(cmd_line.clj:63)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at code_maat.cmd_line.main(Unknown Source)
Error:  Internal error - please report it. Details = churn analysis: the given VCS data doesn't contain modification metrics. Check the code-maat docs for supported VCS and correct log format.

This issue arises from the choice of using SVN's XML log format, which does not support reporting the diffs. The text format supports diffs but may be harder to parse.

@adamtornhill
Copy link
Owner

Yes, you're correct - code churn isn't supported for SVN (only for Git).

We could implement support for the text format to get some basic churn metrics for SVN as well. But IIRC I looked into that a few years ago and found out that SVN only supports a single metric; From the log, there's no way to know if a line was added, modified or deleted, right?

Thus, the way I've approached code churn in SVN is to traverse the repository, svn diff the changed files and calculate detailed churn metrics from that.

@igilham
Copy link
Author

igilham commented Feb 18, 2015

svn log -v --diff prints lines prefixed with a + or - where they have changed. It should be possible to get the churn from that, although it may require a new parser to support it. Perhaps the --diff flag wasn't supported last time you checked?

I wouldn't like to comment on the challenge of reworking other parts of the tool to support the textual log format. Clojure is still very alien to me at the moment.

@adamtornhill
Copy link
Owner

Cool - I wasn't aware of the `diff´ flag. Sounds interesting.

It's quite straightforward to support a new format. The parsers are orthogonal to the analysis algorithms; The analyses don't know anything about the different version-control systems. A parser in Code Maat translates the input to a common format that all analyses operate on. So basically we just need to specify a grammar for the SVN text format and hook it in.

I'd be interested in giving it a try. However, I probably won't be able to do that in the near future. But I put it in my TODO-queue here. Also, if you have some sample SVN log file with churn data, feel free to send it to me. It would be useful.

@igilham
Copy link
Author

igilham commented Feb 19, 2015

How about data from Apache Subversion itself? It's a pretty complex project with code in C, Java and some Python tests.

svn log -v --diff https://svn.apache.org/repos/asf/subversion/trunk/ -r {20150217}:HEAD > svnlog.log

svnlog.log contents:

------------------------------------------------------------------------
r1660541 | rhuijben | 2015-02-17 23:55:11 +0000 (Tue, 17 Feb 2015) | 5 lines
Changed paths:
   M /subversion/trunk/INSTALL

* INSTALL
  Remove some outdated Windows specifics.
  Building zlib with masm support will give you invalid data, so remove
  from file.


Index: INSTALL
===================================================================
--- INSTALL (revision 1660540)
+++ INSTALL (revision 1660541)
@@ -355,10 +355,6 @@
           Under Windows, you can specify the paths to these libraries by
           passing the options --with-zlib and --with-openssl to gen-make.py.

-            ### Is that right? In-tree build of Neon was disabled in r875974.
-                This may now apply to Serf, or else gen-make.py should be
-                updated to remove such options.
-
         c. Using OpenSSL on the Apache server

           You can also add support for these features to an Apache httpd
@@ -465,24 +461,8 @@
       script.


-      12. MASM 6 or newer (Windows only, OPTIONAL)
+      12. SQLite  (REQUIRED)

-      The Windows build scripts for Subversion can use the Microsoft
-      Macro Assembler (MASM) to build an optimized version of the ZLib
-      library. Make sure that the version of MASM you use is compatible
-      with the C compiler. If you're using MSVC 6, and don't have MASM 6,
-      a free MASM-compatible assembler is available here:
-
-          http://www.masm32.com/
-
-      You only need ML.EXE and ML.ERR from this distribution.
-
-      The VS.NET installation already contains MASM (but note, that
-      version if MASM is not compatible with MSVC 6).
-
-
-      13. SQLite  (REQUIRED)
-
       Subversion 1.8 requires SQLite version 3.7.12 or above.  You can meet
       this dependency several ways:
         * Use an SQLite amalgamation file.
@@ -497,19 +477,19 @@
           http://www.sqlite.org/download.html


-      14. pkg-config  (Unix only, OPTIONAL)
+      13. pkg-config  (Unix only, OPTIONAL)

       Subversion uses pkg-config to find appropriate options used
       at build time.


-      15. D-Bus  (Unix only, OPTIONAL)
+      14. D-Bus  (Unix only, OPTIONAL)

       D-Bus is a message bus system. D-Bus is required for support for KWallet
       and GNOME Keyring. pkg-config is needed to find D-Bus headers and library.


-      16. Qt 4  (Unix only, OPTIONAL)
+      15. Qt 4  (Unix only, OPTIONAL)

       Qt is a cross-platform application framework. QtCore, QtDBus and QtGui
       modules are required for support for KWallet. pkg-config is needed
@@ -516,7 +496,7 @@
       to find Qt headers and libraries.


-      17. KDELibs 4  (Unix only, OPTIONAL)
+      16. KDELibs 4  (Unix only, OPTIONAL)

       Subversion contains optional support for storing passwords in KWallet.
       KDELibs contains core KDE libraries. Subversion uses libkdecore and libkdeui
@@ -528,13 +508,13 @@

           --with-kwallet=/path/to/KDE/prefix

-      18. GLib 2  (Unix only, OPTIONAL)
+      17. GLib 2  (Unix only, OPTIONAL)

       GLib is a general-purpose utility library. GLib is required for support
       for GNOME Keyring. pkg-config is needed to find GLib headers and library.


-      19. GNOME Keyring  (Unix only, OPTIONAL)
+      18. GNOME Keyring  (Unix only, OPTIONAL)

       Subversion contains optional support for storing passwords in GNOME Keyring.
       pkg-config is needed to find GNOME Keyring headers and library. D-Bus and
@@ -542,7 +522,7 @@
       then pass the '--with-gnome-keyring' option to `configure`.


-      20. Ctypesgen  (OPTIONAL)
+      19. Ctypesgen  (OPTIONAL)

       Ctypesgen is Python wrapper generator for ctypes. It is used to generate
       a part of Subversion Ctypes Python bindings (CSVN). If you want to build
@@ -553,7 +533,7 @@

       For more information on CSVN, see subversion/bindings/ctypes-python/README.

-      21. libmagic (OPTIONAL)
+      20. libmagic (OPTIONAL)

       Subversion's configure script attempts to find libmagic automatically.
       If it is installed in a non-standard location, then use:
@@ -574,7 +554,7 @@

         --with-libmagic

-      22. Googlemock (OPTIONAL)
+      21. Googlemock (OPTIONAL)

       Googlemock can be installed and built in-tree by invoking


------------------------------------------------------------------------
r1660542 | breser | 2015-02-18 00:01:29 +0000 (Wed, 18 Feb 2015) | 2 lines
Changed paths:
   M /subversion/trunk/INSTALL

* INSTALL: Remove mentions of RPMs that we don't support anymore.


Index: INSTALL
===================================================================
--- INSTALL (revision 1660541)
+++ INSTALL (revision 1660542)
@@ -15,7 +15,7 @@
        D. Documentation

     II. INSTALLATION
-       A. Building from a Tarball or RPM
+       A. Building from a Tarball
        B. Building the Latest Source under Unix
        C. Building under Unix in Different Directories
        D. Installing from a Zip or Installer File under Windows
@@ -574,7 +574,7 @@
 II.   INSTALLATION
       ============

-  A.  Building from a Tarball or RPM
+  A.  Building from a Tarball
       ------------------------------

       1.  Building from a Tarball
@@ -592,31 +592,6 @@
       You can also run the full test suite by running 'make check'.


-      2.  Building from an RPM
-
-      If you are using Linux (or any OS that can use RPM) then another
-      possibility is to download the binary RPM from the
-      http://summersoft.fay.ar.us/pub/subversion directory.
-
-      Currently only Linux on the i386 platform is supported
-      using this method.  You might also require additional RPMS
-      (which can be found in the above mentioned directory) to use the
-      subversion RPM depending on what packages you already have installed:
-
-          subversion*.i386.rpm
-          apache*.i386.rpm (Version 2.2.0 or greater)
-          db*.i386.rpm     (Version 4.0.14 or greater)
-
-      After downloading, install it (as root user):
-
-          # rpm -ivh subversion*.386.rpm (add other packages as necessary)
-
-      Note: For an easy way to generate a new version of the RPM
-      source and binary package from the latest source code you
-      just checked out, see the packages/rpm/README file for a
-      one-line build procedure.
-
-
   B.  Building the Latest Source under Unix
       -------------------------------------

@@ -1137,12 +1112,6 @@
   A.  Setting Up Apache
       -----------------

-      (Following the BOOTSTRAPPING FROM RPM procedures above will install and
-      build the latest Subversion server for Linux RedHat 7.1, 7.2, and PPC
-      Linux systems *IF* the apache-devel-2.0.41 or greater package is already
-      installed when the SUBVERSION RPM is built.)
-
-
       1.  Obtaining and Installing Apache 2

       Subversion tries to compile against the latest released version

------------------------------------------------------------------------
r1660543 | breser | 2015-02-18 00:04:39 +0000 (Wed, 18 Feb 2015) | 2 lines
Changed paths:
   M /subversion/trunk/INSTALL

* INSTALL: Note that BDB is deprecated to discourage usage.


Index: INSTALL
===================================================================
--- INSTALL (revision 1660542)
+++ INSTALL (revision 1660543)
@@ -123,7 +123,8 @@
          create a repository, you have the option of specifying a
          storage back-end.  The Berkeley DB back-end will only be
          available if the BDB libraries are discovered at compile
-         time.
+         time.  The Berkeley DB back-end has been deprecated and
+         is not recommend.

       * libsasl (OPTIONAL for client and server)


------------------------------------------------------------------------
r1660548 | brane | 2015-02-18 00:29:51 +0000 (Wed, 18 Feb 2015) | 11 lines
Changed paths:
   M /subversion/trunk/build/run_tests.py
   M /subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
   M /subversion/trunk/subversion/include/svn_version.h
   M /subversion/trunk/subversion/tests/cmdline/svntest/main.py

Increment the trunk version number to 1.10.0-dev.

* subversion/include/svn_version.h: Increment version number.

* subversion/tests/cmdline/svntest/main.py (SVN_VER_MINOR),
  build/run_tests.py (SVN_VER_MINOR):
    Increment version number.

* subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
  (NativeResources.init): Increment required library version.


Index: subversion/tests/cmdline/svntest/main.py
===================================================================
--- subversion/tests/cmdline/svntest/main.py    (revision 1660547)
+++ subversion/tests/cmdline/svntest/main.py    (revision 1660548)
@@ -54,7 +54,7 @@
 from svntest import Failure
 from svntest import Skip

-SVN_VER_MINOR = 9
+SVN_VER_MINOR = 10

 ######################################################################
 #
Index: subversion/include/svn_version.h
===================================================================
--- subversion/include/svn_version.h    (revision 1660547)
+++ subversion/include/svn_version.h    (revision 1660548)
@@ -61,7 +61,7 @@
  * Modify when new functionality is added or new interfaces are
  * defined, but all changes are backward compatible.
  */
-#define SVN_VER_MINOR      9
+#define SVN_VER_MINOR      10

 /**
  * Patch number.
Index: subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
===================================================================
--- subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java    (revision 1660547)
+++ subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java    (revision 1660548)
@@ -142,10 +142,10 @@
     private static final void init()
     {
         version = new Version();
-        if (!version.isAtLeast(1, 9, 0))
+        if (!version.isAtLeast(1, 10, 0))
         {
             throw new LinkageError("Native library version must be at least " +
-                                   "1.9.0, but is only " + version);
+                                   "1.10.0, but is only " + version);
         }

         runtimeVersion = new RuntimeVersion();
Index: build/run_tests.py
===================================================================
--- build/run_tests.py  (revision 1660547)
+++ build/run_tests.py  (revision 1660548)
@@ -45,7 +45,7 @@
 '''

 # A few useful constants
-SVN_VER_MINOR = 9
+SVN_VER_MINOR = 10

 import os, re, subprocess, sys, imp, threading, traceback, exceptions
 from datetime import datetime

------------------------------------------------------------------------
r1660587 | rhuijben | 2015-02-18 10:46:51 +0000 (Wed, 18 Feb 2015) | 25 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql
   M /subversion/trunk/subversion/tests/libsvn_wc/db-test.c
   M /subversion/trunk/subversion/tests/libsvn_wc/entries-compat.c
   M /subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c
   M /subversion/trunk/subversion/tests/libsvn_wc/wc-queries-test.c
   M /subversion/trunk/subversion/tests/libsvn_wc/wc-test-queries.sql

Fix more cases where the direct db operations in the C tests create nonstandard
db instances.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Extend tests.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Give present directories a depth and remove pristine data
    from local additions that aren't copies.

* subversion/tests/libsvn_wc/entries-compat.c
  (TESTING_DATA): Give present directories a depth; files a checksum and
    remove pristine data from local additions.

* subversion/tests/libsvn_wc/op-depth-test.c
  (insert_actual): When turning a directory in a file, really make it a file.

* subversion/tests/libsvn_wc/wc-queries-test.c
  (test_verify_parsable): New function, verifying the check statements.
  (test_funcs): Add test_verify_parsable.

* subversion/tests/libsvn_wc/wc-test-queries.sql
  (STMT_ENSURE_EMPTY_PRISTINE): New statement.
  (STMT_NODES_SET_FILE): Set checksum, remove depth.


Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660586)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660587)
@@ -134,4 +134,24 @@
 WHERE presence IN (MAP_BASE_DELETED)
 AND (repos_id IS NOT NULL
      OR repos_path IS NOT NULL
-     OR revision IS NOT NULL)
\ No newline at end of file
+     OR revision IS NOT NULL)
+
+UNION ALL
+
+SELECT local_relpath, op_depth, 'SV006: Kind specific data invalid on normal'
+FROM nodes
+WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+AND (kind IS NULL
+     OR (repos_path IS NULL
+         AND (properties IS NOT NULL
+              OR changed_revision IS NOT NULL
+              OR changed_author IS NOT NULL
+              OR (changed_date IS NOT NULL AND changed_date != 0)))
+     OR (CASE WHEN kind = MAP_FILE AND repos_path IS NOT NULL
+                                   THEN checksum IS NULL
+                                   ELSE checksum IS NOT NULL END)
+     OR (CASE WHEN kind = MAP_DIR THEN depth IS NULL
+                                  ELSE depth IS NOT NULL END)
+     OR (CASE WHEN kind = MAP_SYMLINK THEN symlink_target IS NULL
+                                      ELSE symlink_target IS NOT NULL END))
+
Index: subversion/tests/libsvn_wc/entries-compat.c
===================================================================
--- subversion/tests/libsvn_wc/entries-compat.c (revision 1660586)
+++ subversion/tests/libsvn_wc/entries-compat.c (revision 1660587)
@@ -131,15 +131,15 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'I', 0, '', 1, 'I', 1, 'normal',"
-  "  null, null, 'file', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J', 0, '', 1, 'J', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal',"
@@ -147,7 +147,7 @@
   "  15, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-b', 0, 'J/J-e', 1, 'J/J-e/J-e-b', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-b/Jeba', 0, 'J/J-e/J-e-b', 1, 'J/J-e/J-e-b/Jeba', 1, 'normal',"
@@ -155,15 +155,15 @@
   "  15, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f', 0, 'J', 1, 'J/J-f', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f/J-f-a', 0, 'J/J-f', 1, 'J/J-f/J-f-a', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K', 0, '', 1, 'K', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-a', 0, 'K', 1, 'K/K-a', 1, 'normal',"
@@ -179,15 +179,15 @@
       the value 1 is just 'good enough' to make the nodes WORKING nodes. */
   "insert into nodes values ("
   "  1, 'I', 1, '', 2, 'some/dir', 2, 'normal',"
-  "  null, null, 'file', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-a', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'file', '()', null, null, null, null, null, null,"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b', 1, 'J', 2, 'some/dir', 2, 'normal',"
@@ -199,7 +199,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b/J-b-b', 1, 'J/J-b', null, null, null, 'normal',"
-  "  null, null, 'file', '()', null, null, null, null, null, null,"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-c', 1, 'J', null, null, null, 'not-present',"
@@ -231,7 +231,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f/J-f-a', 1, 'J/J-f', null, null, null, 'base-deleted',"
@@ -251,7 +251,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L/L-a', 1, 'L', null, null, null, 'not-present',"
@@ -273,7 +273,7 @@
    "  "
    "insert into nodes values ("
    "  1, 'M', 0, '', 1, 'M', 1, 'normal', "
-   "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "', "
+   "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "', "
    "  null, null, null, null, null);"
    "insert into nodes values ("
    "  1, 'M/M-a', 0, 'M', 1, 'M/M-a', 1, 'not-present', "
Index: subversion/tests/libsvn_wc/op-depth-test.c
===================================================================
--- subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660586)
+++ subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660587)
@@ -2031,6 +2031,9 @@
       SVN_ERR(svn_sqlite__step_done(stmt));
       if (actual->changelist)
         {
+          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                            STMT_ENSURE_EMPTY_PRISTINE));
+          SVN_ERR(svn_sqlite__step_done(stmt));
           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_NODES_SET_FILE));
           SVN_ERR(svn_sqlite__bindf(stmt, "s", actual->local_relpath));
           SVN_ERR(svn_sqlite__step_done(stmt));
Index: subversion/tests/libsvn_wc/wc-test-queries.sql
===================================================================
--- subversion/tests/libsvn_wc/wc-test-queries.sql  (revision 1660586)
+++ subversion/tests/libsvn_wc/wc-test-queries.sql  (revision 1660587)
@@ -58,8 +58,18 @@
 INSERT INTO actual_node (local_relpath, parent_relpath, changelist, wc_id)
                 VALUES (?1, ?2, ?3, 1)

+-- STMT_ENSURE_EMPTY_PRISTINE
+INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount)
+  VALUES ('$sha1$da39a3ee5e6b4b0d3255bfef95601890afd80709',
+          '$md5 $d41d8cd98f00b204e9800998ecf8427e',
+          0, 0)
+
 -- STMT_NODES_SET_FILE
-UPDATE nodes SET kind = 'file' WHERE wc_id = 1 and local_relpath = ?1
+UPDATE nodes
+   SET kind = 'file',
+       checksum = '$sha1$da39a3ee5e6b4b0d3255bfef95601890afd80709',
+       depth = NULL
+WHERE wc_id = 1 and local_relpath = ?1

 -- STMT_SELECT_ALL_ACTUAL
 SELECT local_relpath FROM actual_node WHERE wc_id = 1
Index: subversion/tests/libsvn_wc/wc-queries-test.c
===================================================================
--- subversion/tests/libsvn_wc/wc-queries-test.c    (revision 1660586)
+++ subversion/tests/libsvn_wc/wc-queries-test.c    (revision 1660587)
@@ -984,6 +984,47 @@
   return SVN_NO_ERROR;
 }

+/* Parse all verify/check queries */
+static svn_error_t *
+test_verify_parsable(apr_pool_t *scratch_pool)
+{
+  sqlite3 *sdb;
+  int i;
+
+  SVN_ERR(create_memory_db(&sdb, scratch_pool));
+
+  for (i=STMT_VERIFICATION_TRIGGERS; wc_queries[i]; i++)
+    {
+      sqlite3_stmt *stmt;
+      const char *text = wc_queries[i];
+
+      /* Some of our statement texts contain multiple queries. We prepare
+         them all. */
+      while (*text != '\0')
+        {
+          const char *tail;
+          int r = sqlite3_prepare_v2(sdb, text, -1, &stmt, &tail);
+
+          if (r != SQLITE_OK)
+            return svn_error_createf(SVN_ERR_SQLITE_ERROR, NULL,
+                                     "Preparing %s failed: %s\n%s",
+                                     wc_query_info[i][0],
+                                     sqlite3_errmsg(sdb),
+                                     text);
+
+          SQLITE_ERR(sqlite3_finalize(stmt));
+
+          /* Continue after the current statement */
+          text = tail;
+        }
+    }
+
+  SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */
+
+  return SVN_NO_ERROR;
+}
+
+
 static int max_threads = 1;

 static struct svn_test_descriptor_t test_funcs[] =
@@ -999,6 +1040,8 @@
                    "test query duplicates"),
     SVN_TEST_PASS2(test_schema_statistics,
                    "test schema statistics"),
+    SVN_TEST_PASS2(test_verify_parsable,
+                   "verify queries are parsable"),
     SVN_TEST_NULL
   };

Index: subversion/tests/libsvn_wc/db-test.c
===================================================================
--- subversion/tests/libsvn_wc/db-test.c    (revision 1660586)
+++ subversion/tests/libsvn_wc/db-test.c    (revision 1660587)
@@ -132,15 +132,15 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'I', 0, '', 1, 'I', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J', 0, '', 1, 'J', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal',"
-  "  null, 'other/place', 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, 'other/place', 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal',"
@@ -148,7 +148,7 @@
   "  15, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-b', 0, 'J/J-e', 1, 'J/J-e/J-e-b', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-b/Jeba', 0, 'J/J-e/J-e-b', 1, 'J/J-e/J-e-b/Jeba', 1, 'normal',"
@@ -156,15 +156,15 @@
   "  15, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f', 0, 'J', 1, 'J/J-f', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f/J-f-a', 0, 'J/J-f', 1, 'J/J-f/J-f-a', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K', 0, '', 1, 'K', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-a', 0, 'K', 1, 'K/K-a', 1, 'normal',"
@@ -189,15 +189,15 @@
       implies they are children of a copied J. */
   "insert into nodes values ("
   "  1, 'J', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-a', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'file', '()', null, null, null, null, null, null,"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b', 2, 'J', 2, 'some/dir', 2, 'normal',"
-  "  null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  null, null, 'dir', null, 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b/J-b-a', 3, 'J/J-b', 2, 'another/dir', 2, 'normal',"
@@ -205,15 +205,15 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b/J-b-b', 2, 'J/J-b', null, null, 2, 'normal',"
-  "  null, null, 'file', '()', null, null, null, null, null, null,"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-c', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-c/J-c-a', 1, 'J/J-c', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-c', 2, 'J', null, null, null, 'base-deleted',"
@@ -229,7 +229,7 @@
   "  10, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'moved', 0, '', 2, 'moved', 2, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'moved/file', 0, 'moved', 2, 'moved/file', 2, 'normal',"
@@ -241,15 +241,15 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'normal',"
-  "  null, null, 'file', '()', null, null, null, null, null, null,"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 2, 'J', null, null, null, 'base-deleted',"
@@ -269,7 +269,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f/J-f-a', 1, 'J/J-f', null, null, null, 'base-deleted',"
@@ -289,15 +289,15 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L/L-a', 1, 'L', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L/L-a', 2, 'L', null, null, null, 'base-deleted',"
@@ -309,24 +309,24 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'other', 0, '', 2, 'other', 2, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, null, null, null,"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'other/place', 2, 'other', null, null, null, 'normal',"
-  "  1, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  1, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'other/place/J-e-a', 2, 'other/place', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'other/place/J-e-b', 2, 'other/place', null, null, null, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'other/place/J-e-b/Jeba', 0, 'other/place/J-e-b', null, null, null, 'normal',"
-  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
-  "  15, null, null, null, null);"
+  "  null, null, 'file', null, null, null, null, null, null, null,"
+  "  null, null, null, null, null);"
    "insert into actual_node values ("
    "  1, 'A', '', null, null, null, null, null, 'changelist', null, "
    "  null, null, null, null, null);"

------------------------------------------------------------------------
r1660593 | rhuijben | 2015-02-18 11:08:41 +0000 (Wed, 18 Feb 2015) | 25 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/adm_ops.c
   M /subversion/trunk/subversion/libsvn_wc/update_editor.c
   M /subversion/trunk/subversion/libsvn_wc/wc_db.c
   M /subversion/trunk/subversion/libsvn_wc/wc_db.h
   M /subversion/trunk/subversion/tests/cmdline/copy_tests.py

Stop setting last-rev, last-date and last-author on foreign repos copies
and merges. Fix consistency between svn_wc__db_op_copy_XXX() methods.

This fixes a bug in our entries compatibility code, as that assumes that
there is a copy if there is a last-rev.

* subversion/libsvn_wc/adm_ops.c
  (svn_wc_add4): Update caller.

* subversion/libsvn_wc/update_editor.c
  (svn_wc__complete_directory_add): Update caller.

* subversion/libsvn_wc/wc_db.c
  (insert_working_node): Don't set pristine properties on local additions.
  (svn_wc__db_op_copy_dir): Move is_moved argument last. Don't set changed*
    and properties on local additions.
  (svn_wc__db_op_copy_file): Don't set changed* and properties on local
    additions.
  (svn_wc__db_op_copy_symlink): Calulate moved_to like the other operations.
    Don't set changed* and properties on local
    additions.

* subversion/tests/cmdline/copy_tests.py
  (repos_to_wc): Fix test expectation.


Index: subversion/tests/cmdline/copy_tests.py
===================================================================
--- subversion/tests/cmdline/copy_tests.py  (revision 1660592)
+++ subversion/tests/cmdline/copy_tests.py  (revision 1660593)
@@ -1005,8 +1005,7 @@

   expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_output.add({
-    'pi' : Item(status='A ',  wc_rev='0', entry_rev='1'),
-    # And from the foreign repository
+    'pi'            : Item(status='A ', wc_rev='0'),
     'E'             : Item(status='A ', wc_rev='0'),
     'E/beta'        : Item(status='A ', wc_rev='0'),
     'E/alpha'       : Item(status='A ', wc_rev='0'),
Index: subversion/libsvn_wc/adm_ops.c
===================================================================
--- subversion/libsvn_wc/adm_ops.c  (revision 1660592)
+++ subversion/libsvn_wc/adm_ops.c  (revision 1660593)
@@ -680,7 +680,8 @@
                                          repos_relpath,
                                          repos_root_url, repos_uuid,
                                          copyfrom_rev,
-                                         NULL /* children */, FALSE, depth,
+                                         NULL /* children */, depth,
+                                         FALSE /* is_move */,
                                          NULL /* conflicts */,
                                          NULL /* work items */,
                                          scratch_pool));
Index: subversion/libsvn_wc/update_editor.c
===================================================================
--- subversion/libsvn_wc/update_editor.c    (revision 1660592)
+++ subversion/libsvn_wc/update_editor.c    (revision 1660593)
@@ -5685,8 +5685,8 @@
                                    original_repos_relpath, original_root_url,
                                    original_uuid, original_revision,
                                    NULL /* children */,
+                                   svn_depth_infinity,
                                    FALSE /* is_move */,
-                                   svn_depth_infinity,
                                    NULL /* conflict */,
                                    NULL /* work_items */,
                                    scratch_pool));
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c    (revision 1660592)
+++ subversion/libsvn_wc/wc_db.c    (revision 1660593)
@@ -1098,7 +1098,7 @@
   assert(piwb->presence == svn_wc__db_status_normal
          || piwb->presence == svn_wc__db_status_incomplete
          || piwb->props == NULL);
-  if (present)
+  if (present && piwb->original_repos_relpath)
     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));

   SVN_ERR(svn_sqlite__insert(NULL, stmt));
@@ -5668,8 +5668,8 @@
                        const char *original_uuid,
                        svn_revnum_t original_revision,
                        const apr_array_header_t *children,
+                       svn_depth_t depth,
                        svn_boolean_t is_move,
-                       svn_depth_t depth,
                        const svn_skel_t *conflict,
                        const svn_skel_t *work_items,
                        apr_pool_t *scratch_pool)
@@ -5696,11 +5696,6 @@
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_dir;

-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-
   if (original_root_url != NULL)
     {
       SVN_ERR(create_repos_id(&iwb.original_repos_id,
@@ -5708,6 +5703,11 @@
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }

   /* ### Should we do this inside the transaction? */
@@ -5776,11 +5776,6 @@
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_file;

-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-
   if (original_root_url != NULL)
     {
       SVN_ERR(create_repos_id(&iwb.original_repos_id,
@@ -5788,6 +5783,11 @@
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }

   /* ### Should we do this inside the transaction? */
@@ -5830,6 +5830,7 @@
                            const char *original_uuid,
                            svn_revnum_t original_revision,
                            const char *target,
+                           svn_boolean_t is_move,
                            const svn_skel_t *conflict,
                            const svn_skel_t *work_items,
                            apr_pool_t *scratch_pool)
@@ -5854,11 +5855,6 @@
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_symlink;

-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-  iwb.moved_here = FALSE;

   if (original_root_url != NULL)
     {
@@ -5867,6 +5863,11 @@
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }

   /* ### Should we do this inside the transaction? */
@@ -5876,6 +5877,8 @@
                             wcroot, local_relpath, scratch_pool));

   iwb.target = target;
+  iwb.moved_here = is_move && (parent_op_depth == 0 ||
+                               iwb.op_depth == parent_op_depth);

   iwb.work_items = work_items;
   iwb.conflict = conflict;
Index: subversion/libsvn_wc/wc_db.h
===================================================================
--- subversion/libsvn_wc/wc_db.h    (revision 1660592)
+++ subversion/libsvn_wc/wc_db.h    (revision 1660593)
@@ -1421,8 +1421,8 @@
                        const char *original_uuid,
                        svn_revnum_t original_revision,
                        const apr_array_header_t *children,
+                       svn_depth_t depth,
                        svn_boolean_t is_move,
-                       svn_depth_t depth,
                        const svn_skel_t *conflict,
                        const svn_skel_t *work_items,
                        apr_pool_t *scratch_pool);
@@ -1463,6 +1463,7 @@
                            const char *original_uuid,
                            svn_revnum_t original_revision,
                            const char *target,
+                           svn_boolean_t is_move,
                            const svn_skel_t *conflict,
                            const svn_skel_t *work_items,
                            apr_pool_t *scratch_pool);

------------------------------------------------------------------------
r1660610 | rhuijben | 2015-02-18 12:24:05 +0000 (Wed, 18 Feb 2015) | 19 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_subr/sqlite.c
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql
   M /subversion/trunk/subversion/tests/libsvn_wc/wc-queries-test.c

Make it possible to run sqlite queries that use user defined functions
when the child pools of the pool containing the sqlite database
are being cleaned up.

This is necessary to use op-depth calculations in verification queries.
(or to run queries at all when the verification triggers are used)

* subversion/libsvn_subr/sqlite.c
  (clear_sqlite_function): New function.
  (svn_sqlite__create_scalar_function): Document nasty problem.
    Create function pool in global pool.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Extend verifications.

* subversion/tests/libsvn_wc/wc-queries-test.c
  (relpath_depth_sqlite): New function (stub).
  (test_verify_parsable): Define relpath_depth.


Index: subversion/libsvn_subr/sqlite.c
===================================================================
--- subversion/libsvn_subr/sqlite.c (revision 1660609)
+++ subversion/libsvn_subr/sqlite.c (revision 1660610)
@@ -1515,6 +1515,16 @@
     }
 }

+/* pool cleanup function for function context */
+static apr_status_t
+clear_sqlite_function(void *baton)
+{
+  struct function_wrapper_baton_t *fwb = baton;
+
+  svn_pool_destroy(fwb->scratch_pool);
+  return APR_SUCCESS;
+}
+
 svn_error_t *
 svn_sqlite__create_scalar_function(svn_sqlite__db_t *db,
                                    const char *func_name,
@@ -1527,7 +1537,14 @@
   struct function_wrapper_baton_t *fwb = apr_pcalloc(db->state_pool,
                                                      sizeof(*fwb));

-  fwb->scratch_pool = svn_pool_create(db->state_pool);
+  /* If we create our scratch pool in db->state pool it is cleared
+     before the database is closed. This breaks using queries
+     before we close the database :(
+
+     We create a subpool in the global pool and only destroy it
+     when we want it to be destroyed */
+
+  fwb->scratch_pool = svn_pool_create(NULL);
   fwb->func = func;
   fwb->baton = baton;

@@ -1539,6 +1556,9 @@
                                      fwb, wrapped_func, NULL, NULL),
              db);

+  apr_pool_cleanup_register(db->state_pool, fwb, clear_sqlite_function,
+                            apr_pool_cleanup_null);
+
   return SVN_NO_ERROR;
 }

Index: subversion/tests/libsvn_wc/wc-queries-test.c
===================================================================
--- subversion/tests/libsvn_wc/wc-queries-test.c    (revision 1660609)
+++ subversion/tests/libsvn_wc/wc-queries-test.c    (revision 1660610)
@@ -984,6 +984,15 @@
   return SVN_NO_ERROR;
 }

+/* An SQLite application defined function that allows SQL queries to
+   use "relpath_depth(local_relpath)".  */
+static void relpath_depth_sqlite(sqlite3_context* context,
+                                 int argc,
+                                 sqlite3_value* values[])
+{
+  SVN_ERR_MALFUNCTION_NO_RETURN(); /* STUB! */
+}
+
 /* Parse all verify/check queries */
 static svn_error_t *
 test_verify_parsable(apr_pool_t *scratch_pool)
@@ -993,6 +1002,9 @@

   SVN_ERR(create_memory_db(&sdb, scratch_pool));

+  SQLITE_ERR(sqlite3_create_function(sdb, "relpath_depth", 1, SQLITE_ANY, NULL,
+                                     relpath_depth_sqlite, NULL, NULL));
+
   for (i=STMT_VERIFICATION_TRIGGERS; wc_queries[i]; i++)
     {
       sqlite3_stmt *stmt;
Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660609)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660610)
@@ -155,3 +155,36 @@
      OR (CASE WHEN kind = MAP_SYMLINK THEN symlink_target IS NULL
                                       ELSE symlink_target IS NOT NULL END))

+UNION ALL
+
+SELECT local_relpath, op_depth, 'SV007: Invalid op-depth for local add'
+FROM nodes
+WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+  AND repos_path IS NULL
+  AND op_depth != relpath_depth(local_relpath)
+
+UNION ALL
+
+SELECT local_relpath, op_depth, 'SV008: Node missing ancestor'
+FROM nodes n
+WHERE op_depth < relpath_depth(local_relpath)
+  AND file_external IS NULL
+  AND NOT EXISTS(SELECT 1 FROM nodes p
+                 WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath
+                   AND p.op_depth=n.op_depth
+                   AND (p.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+                        OR (p.presence = MAP_BASE_DELETED
+                            AND n.presence = MAP_BASE_DELETED)))
+
+UNION all
+
+SELECT n.local_relpath, n.op_depth, 'SV009: Copied descendant mismatch'
+FROM nodes n
+JOIN nodes p
+  ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath
+  AND n.op_depth=p.op_depth
+WHERE n.op_depth > 0 AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+   AND (n.repos_id != p.repos_id
+        OR n.repos_path !=
+           RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath)
+        OR n.revision != p.revision)

------------------------------------------------------------------------
r1660633 | rhuijben | 2015-02-18 13:55:39 +0000 (Wed, 18 Feb 2015) | 21 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql
   M /subversion/trunk/subversion/tests/libsvn_wc/db-test.c

Fix the op-depth calculations in db-tests.c, to no longer trip +-
every validation rule of op-depths. This involved changing
a local added subdir (with descendants at the same op-depth!)
into a copy.

These testcases were written before NODES introduced op-depth,
and the tests were only updated to not trigger test failures.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Make ancestor check a bit stricter.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Tweak setup.
  (test_pdh): Install ancestor for not-present to avoid
    error.
  (validate_abspath): Tweak function to print paths on error.
  (test_scan_addition): Tweak expectations after transforming
    add into a copy.
  (test_scan_deletion): Tweak test. Minor change to handle
    working delete.


Index: subversion/tests/libsvn_wc/db-test.c
===================================================================
--- subversion/tests/libsvn_wc/db-test.c    (revision 1660632)
+++ subversion/tests/libsvn_wc/db-test.c    (revision 1660633)
@@ -183,21 +183,19 @@
   "  null, null, 'dir', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"

-   /* I'm not sure what the working J is supposed to represent.  It
-      replaces the base J, but is it a copy or not?  It has no
-      copyfrom, but nodes like J/J-e appear to be deleted which
-      implies they are children of a copied J. */
+   /* J was originally a local addition, but its descendants are replaced,
+      so let's turn J in a copy */
   "insert into nodes values ("
-  "  1, 'J', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
+  "  1, 'J', 1, '', 2, 'q', 2, 'normal',"
+  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-a', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'file', null, null, null, null, null, null, null,"
+  "  1, 'J/J-a', 1, 'J', 2, 'q/J-a', 2, 'normal',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-b', 2, 'J', 2, 'some/dir', 2, 'normal',"
-  "  null, null, 'dir', null, 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  1, 'J/J-b', 1, 'J', 2, 'q/J-b', 2, 'normal',"
+  "  null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-b/J-b-a', 3, 'J/J-b', 2, 'another/dir', 2, 'normal',"
@@ -204,16 +202,16 @@
   "  null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-b/J-b-b', 2, 'J/J-b', null, null, 2, 'normal',"
-  "  null, null, 'file', null, null, null, null, null, null, null,"
+  "  1, 'J/J-b/J-b-b', 1, 'J/J-b', 2, 'q/J-b/J-b-b', 2, 'normal',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-c', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
+  "  1, 'J/J-c', 1, 'J', 2, 'q/J-c', 2, 'normal',"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-c/J-c-a', 1, 'J/J-c', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
+  "  1, 'J/J-c/J-c-a', 1, 'J/J-c', 2, 'q/J-c/J-c-a', 2, 'normal',"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-c', 2, 'J', null, null, null, 'base-deleted',"
@@ -240,16 +238,16 @@
   "  null, 'J/J-d', 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
+  "  1, 'J/J-e', 1, 'J', 2, 'q/J-e', 2, 'normal',"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'normal',"
-  "  null, null, 'file', null, null, null, null, null, null, null,"
+  "  1, 'J/J-e/J-e-a', 1, 'J/J-e', 2, 'q/J-e/J-e-a', 2, 'normal',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
+  "  1, 'J/J-e/J-e-b', 1, 'J/J-e', 2, 'q/J-e/J-e-b', 2, 'normal',"
+  "  null, null, 'dir', '()', 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 2, 'J', null, null, null, 'base-deleted',"
@@ -264,12 +262,12 @@
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-b/Jeba', 1, 'J/J-e/J-e-b', null, null, null, 'base-deleted',"
+  "  1, 'J/J-e/J-e-b/Jeba', 2, 'J/J-e/J-e-b', null, null, null, 'base-deleted',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-f', 1, 'J', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
+  "  1, 'J/J-f', 1, 'J', 2, 'q/J-f', 2, 'normal',"
+  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-f/J-f-a', 1, 'J/J-f', null, null, null, 'base-deleted',"
@@ -288,16 +286,16 @@
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'L', 1, '', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
+  "  1, 'L', 1, '', 2, 'from', 2, 'normal',"
+  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'L/L-a', 1, 'L', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
+  "  1, 'L/L-a', 1, 'L', 2, 'from/L-a', 2, 'normal',"
+  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'normal',"
-  "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
+  "  1, 'L/L-a/L-a-a', 1, 'L/L-a', 2, 'from/L-a/L-a-a', 2, 'normal',"
+  "  null, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L/L-a', 2, 'L', null, null, null, 'base-deleted',"
@@ -316,15 +314,15 @@
   "  1, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'other/place/J-e-a', 2, 'other/place', null, null, null, 'normal',"
+  "  1, 'other/place/J-e-a', 3, 'other/place', null, null, null, 'normal',"
   "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'other/place/J-e-b', 2, 'other/place', null, null, null, 'normal',"
+  "  1, 'other/place/J-e-b', 3, 'other/place', null, null, null, 'normal',"
   "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'other/place/J-e-b/Jeba', 0, 'other/place/J-e-b', null, null, null, 'normal',"
+  "  1, 'other/place/J-e-b/Jeba', 4, 'other/place/J-e-b', null, null, null, 'normal',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
    "insert into actual_node values ("
@@ -374,18 +372,17 @@
 }


-static svn_boolean_t
+static svn_error_t *
 validate_abspath(const char *wcroot_abspath,
                  const char *expected_relpath,
                  const char *actual_abspath,
                  apr_pool_t *scratch_pool)
 {
-  if (actual_abspath == NULL)
-    return FALSE;
-  return strcmp(svn_dirent_join(wcroot_abspath,
+  SVN_TEST_STRING_ASSERT(actual_abspath,
+                         svn_dirent_join(wcroot_abspath,
                                 expected_relpath,
-                                scratch_pool),
-                actual_abspath) == 0;
+                                scratch_pool));
+  return SVN_NO_ERROR;
 }


@@ -949,9 +946,16 @@
             NULL, NULL,
             pool));

+  SVN_ERR(svn_wc__db_base_add_directory(
+            db, svn_dirent_join(local_abspath, "sub2", pool),
+            local_abspath, "sub2", ROOT_ONE, UUID_ONE, 1,
+            apr_hash_make(pool), 1, 1, "me", NULL,
+            svn_depth_infinity, NULL, FALSE, NULL, NULL,
+            NULL, NULL, pool));
+
   SVN_ERR(svn_wc__db_base_add_excluded_node(
-            db, svn_dirent_join(local_abspath, "sub/A", pool),
-            "sub/A", ROOT_ONE, UUID_ONE, 1,
+            db, svn_dirent_join(local_abspath, "sub2/A", pool),
+            "sub2/A", ROOT_ONE, UUID_ONE, 1,
             svn_node_file, svn_wc__db_status_server_excluded,
             NULL, NULL,
             pool));
@@ -989,17 +993,17 @@
             &original_revision,
             db, svn_dirent_join(local_abspath, "J", pool),
             pool, pool));
-  SVN_TEST_ASSERT(status == svn_wc__db_status_added);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J", op_root_abspath, pool));
+  SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
+  SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
   SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE);
-  SVN_TEST_ASSERT(original_repos_relpath == NULL);
-  SVN_TEST_ASSERT(original_root_url == NULL);
-  SVN_TEST_ASSERT(original_uuid == NULL);
-  SVN_TEST_ASSERT(original_revision == SVN_INVALID_REVNUM);
+  SVN_TEST_STRING_ASSERT(original_repos_relpath, "q");
+  SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO);
+  SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO);
+  SVN_TEST_ASSERT(original_revision == 2);

-  /* Simple addition of a file (affects how scan-up is started). */
+  /* Simple copy (affects how scan-up is started). */
   SVN_ERR(svn_wc__db_scan_addition(
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
@@ -1007,15 +1011,15 @@
             &original_revision,
             db, svn_dirent_join(local_abspath, "J/J-a", pool),
             pool, pool));
-  SVN_TEST_ASSERT(status == svn_wc__db_status_added);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J", op_root_abspath, pool));
+  SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
+  SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-a");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
   SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE);
-  SVN_TEST_ASSERT(original_repos_relpath == NULL);
-  SVN_TEST_ASSERT(original_root_url == NULL);
-  SVN_TEST_ASSERT(original_uuid == NULL);
-  SVN_TEST_ASSERT(original_revision == SVN_INVALID_REVNUM);
+  SVN_TEST_STRING_ASSERT(original_repos_relpath, "q");
+  SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO);
+  SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO);
+  SVN_TEST_ASSERT(original_revision == 2);

   /* Node was moved here. */
   SVN_ERR(svn_wc__db_scan_addition(
@@ -1033,15 +1037,15 @@
             db, svn_dirent_join(local_abspath, "J/J-d", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_moved_here);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-d",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-d",
                                    op_root_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file",
+  SVN_ERR(validate_abspath(local_abspath, "moved/file",
                                    moved_from_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-d",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-d",
                                    move_op_root_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file",
+  SVN_ERR(validate_abspath(local_abspath, "moved/file",
                                    move_op_root_src, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file",
+  SVN_ERR(validate_abspath(local_abspath, "moved/file",
                                    delete_op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-d");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
@@ -1060,12 +1064,12 @@
             db, svn_dirent_join(local_abspath, "J/J-b", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-b",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
   SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE);
-  SVN_TEST_STRING_ASSERT(original_repos_relpath, "some/dir");
+  SVN_TEST_STRING_ASSERT(original_repos_relpath, "q");
   SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO);
   SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO);
   SVN_TEST_ASSERT(original_revision == 2);
@@ -1079,7 +1083,7 @@
             db, svn_dirent_join(local_abspath, "J/J-b/J-b-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-b/J-b-a",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-b/J-b-a",
                                    op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b/J-b-a");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
@@ -1098,12 +1102,12 @@
             db, svn_dirent_join(local_abspath, "J/J-b/J-b-b", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-b",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b/J-b-b");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
   SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE);
-  SVN_TEST_STRING_ASSERT(original_repos_relpath, "some/dir");
+  SVN_TEST_STRING_ASSERT(original_repos_relpath, "q");
   SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO);
   SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO);
   SVN_TEST_ASSERT(original_revision == 2);
@@ -1132,13 +1136,13 @@
             &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "J/J-e", pool),
             pool, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+  SVN_ERR(validate_abspath(local_abspath, "other/place",
                                    moved_to_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-e",
                                    work_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+  SVN_ERR(validate_abspath(local_abspath, "other/place",
                                    copy_op_root_abspath, pool));

   /* Node was moved elsewhere (child of operation root). */
@@ -1149,13 +1153,13 @@
             &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "J/J-e/J-e-a", pool),
             pool, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-a",
+  SVN_ERR(validate_abspath(local_abspath, "other/place/J-e-a",
                                    moved_to_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-e",
                                    work_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+  SVN_ERR(validate_abspath(local_abspath, "other/place",
                                    copy_op_root_abspath, pool));

   /* Root of delete. Parent is a WORKING node. */
@@ -1167,10 +1171,10 @@
             db, svn_dirent_join(local_abspath, "J/J-c", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-c",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-c",
                                    work_del_abspath, pool));

   /* Child of a deleted root. */
@@ -1182,10 +1186,10 @@
             db, svn_dirent_join(local_abspath, "J/J-c/J-c-a", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-c",
+  SVN_ERR(validate_abspath(local_abspath, "J/J-c",
                                    work_del_abspath, pool));

   /* Base-deleted tree extending past deleted WORKING subtree.  */
@@ -1199,11 +1203,12 @@
   /* ### I don't understand this.  "J/J-e/J-e-b/Jeba" is a deleted
      base node that is not overlayed by the replacement rooted at "J".
      Why does base_del_abspath refer to "J-e"?  */
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-b/Jeba",
+  SVN_ERR(validate_abspath(local_abspath, "other/place/J-e-b/Jeba",
                                    moved_to_abspath, pool));
-  SVN_TEST_ASSERT(work_del_abspath == NULL);
+  SVN_ERR(validate_abspath(local_abspath, "J/J-e",
+                                   work_del_abspath, pool));

   /* Base-deleted tree extending past added WORKING tree.  */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1214,7 +1219,7 @@
             db, svn_dirent_join(local_abspath, "J/J-f/J-f-a", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "J",
+  SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
   SVN_TEST_ASSERT(work_del_abspath == NULL);
@@ -1227,7 +1232,7 @@
             NULL,
             db, svn_dirent_join(local_abspath, "K", pool),
             pool, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "K",
+  SVN_ERR(validate_abspath(local_abspath, "K",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
   SVN_TEST_ASSERT(work_del_abspath == NULL);
@@ -1240,7 +1245,7 @@
             NULL,
             db, svn_dirent_join(local_abspath, "K/K-a", pool),
             pool, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "K",
+  SVN_ERR(validate_abspath(local_abspath, "K",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
   SVN_TEST_ASSERT(work_del_abspath == NULL);
@@ -1253,11 +1258,11 @@
             &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "K/K-b", pool),
             pool, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "K",
+  SVN_ERR(validate_abspath(local_abspath, "K",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away",
+  SVN_ERR(validate_abspath(local_abspath, "moved/away",
                                    moved_to_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away",
+  SVN_ERR(validate_abspath(local_abspath, "moved/away",
                                    copy_op_root_abspath, pool));
   SVN_TEST_ASSERT(work_del_abspath == NULL);

@@ -1271,7 +1276,7 @@
             pool, pool));
   SVN_TEST_ASSERT(base_del_abspath == NULL);
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "L/L-a",
+  SVN_ERR(validate_abspath(local_abspath, "L/L-a",
                                    work_del_abspath, pool));

   /* Subtree deletion of added tree. Start at root.  */
@@ -1284,7 +1289,7 @@
             pool, pool));
   SVN_TEST_ASSERT(base_del_abspath == NULL);
   SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "L/L-a",
+  SVN_ERR(validate_abspath(local_abspath, "L/L-a",
                                    work_del_abspath, pool));

   return SVN_NO_ERROR;
Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660632)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660633)
@@ -81,7 +81,8 @@
  AND file_external IS NULL
  AND NOT EXISTS(SELECT 1 from nodes i
                 WHERE i.wc_id=n.wc_id
-                  AND i.local_relpath=n.parent_relpath)
+                  AND i.local_relpath=n.parent_relpath
+                  AND i.op_depth <= n.op_depth)

 UNION ALL


------------------------------------------------------------------------
r1660641 | rhuijben | 2015-02-18 14:53:56 +0000 (Wed, 18 Feb 2015) | 14 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_subr/sqlite.c

Following up on r1660610, add another pool cleanup handler to
handle the case where the root pool is cleaned up before
the state pool.

This should fix the problems in the swig bindings.

* subversion/libsvn_subr/sqlite.c
  (function_wrapper_baton_t): Hold state pool reference.
  (clear_sqlite_function_scratch): Add forward definition.
  (clear_sqlite_function): Rename to...
  (clear_sqlite_function_state): ... this. Reset registration.
  (clear_sqlite_function_scratch): New function.
  (svn_sqlite__create_scalar_function): Add second registration.


Index: subversion/libsvn_subr/sqlite.c
===================================================================
--- subversion/libsvn_subr/sqlite.c (revision 1660640)
+++ subversion/libsvn_subr/sqlite.c (revision 1660641)
@@ -1479,6 +1479,7 @@
   void *baton;

   apr_pool_t *scratch_pool;
+  apr_pool_t *state_pool;
 };

 static void
@@ -1515,16 +1516,35 @@
     }
 }

-/* pool cleanup function for function context */
+/* Forward definition */
+static apr_status_t clear_sqlite_function_scratch(void *baton);
+
+/* pool cleanup function for function context on state pool */
 static apr_status_t
-clear_sqlite_function(void *baton)
+clear_sqlite_function_state(void *baton)
 {
   struct function_wrapper_baton_t *fwb = baton;

   svn_pool_destroy(fwb->scratch_pool);
+
+  apr_pool_cleanup_kill(fwb->scratch_pool, baton,
+                        clear_sqlite_function_scratch);
+
   return APR_SUCCESS;
 }

+/* pool cleanup function for global cleanup on internal pool */
+static apr_status_t
+clear_sqlite_function_scratch(void *baton)
+{
+  struct function_wrapper_baton_t *fwb = baton;
+
+  apr_pool_cleanup_kill(fwb->state_pool, baton,
+                        clear_sqlite_function_state);
+
+  return APR_SUCCESS;
+}
+
 svn_error_t *
 svn_sqlite__create_scalar_function(svn_sqlite__db_t *db,
                                    const char *func_name,
@@ -1544,6 +1564,7 @@
      We create a subpool in the global pool and only destroy it
      when we want it to be destroyed */

+  fwb->state_pool = db->state_pool;
   fwb->scratch_pool = svn_pool_create(NULL);
   fwb->func = func;
   fwb->baton = baton;
@@ -1556,8 +1577,12 @@
                                      fwb, wrapped_func, NULL, NULL),
              db);

-  apr_pool_cleanup_register(db->state_pool, fwb, clear_sqlite_function,
+  apr_pool_cleanup_register(fwb->state_pool, fwb,
+                            clear_sqlite_function_state,
                             apr_pool_cleanup_null);
+  apr_pool_cleanup_register(fwb->scratch_pool, fwb,
+                            clear_sqlite_function_scratch,
+                            apr_pool_cleanup_null);

   return SVN_NO_ERROR;
 }

------------------------------------------------------------------------
r1660646 | rhuijben | 2015-02-18 15:11:38 +0000 (Wed, 18 Feb 2015) | 13 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/entries.c

Fix an ugly corner case in the upgrade from entries code where replaced
directories would receive an invalid repository location.

In general WC-NG wouldn't use the value, as they are never recorded
on an op-root, and commit only uses these paths from op-roots.

This case tripped the validation rules from upgrade tests #16.

* subversion/libsvn_wc/entries.c
  (write_entry): Fix repository location for directories.
    entry->name was "" for the directory itself. Use revision
    from same source as repos-path and repos-id.


Index: subversion/libsvn_wc/entries.c
===================================================================
--- subversion/libsvn_wc/entries.c  (revision 1660645)
+++ subversion/libsvn_wc/entries.c  (revision 1660646)
@@ -2135,6 +2135,7 @@
       below_working_node->presence = svn_wc__db_status_normal;
       below_working_node->kind = entry->kind;
       below_working_node->repos_id = work->repos_id;
+      below_working_node->revision = work->revision;

       /* This is just guessing. If the node below would have been switched
          or if it was updated to a different version, the guess would
@@ -2141,11 +2142,11 @@
          fail. But we don't have better information pre wc-ng :( */
       if (work->repos_relpath)
         below_working_node->repos_relpath
-          = svn_relpath_join(work->repos_relpath, entry->name,
+          = svn_relpath_join(work->repos_relpath,
+                             svn_relpath_basename(local_relpath, NULL),
                              result_pool);
       else
         below_working_node->repos_relpath = NULL;
-      below_working_node->revision = parent_node->work->revision;

       /* The revert_base checksum isn't available in the entry structure,
          so the caller provides it. */

------------------------------------------------------------------------
r1660652 | rhuijben | 2015-02-18 15:26:24 +0000 (Wed, 18 Feb 2015) | 4 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/entries.c

* subversion/libsvn_wc/entries.c
  (insert_node): Don't write last_* values on local additions as that
    trips the validation rules (and makes no sense).


Index: subversion/libsvn_wc/entries.c
===================================================================
--- subversion/libsvn_wc/entries.c  (revision 1660651)
+++ subversion/libsvn_wc/entries.c  (revision 1660652)
@@ -1459,7 +1459,7 @@
   SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);

   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnris",
+  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn",
                             node->wc_id,
                             node->local_relpath,
                             node->op_depth,
@@ -1467,11 +1467,15 @@
                             /* Setting depth for files? */
                             (node->kind == svn_node_dir && present)
                               ? svn_depth_to_word(node->depth)
-                              : NULL,
-                            node->changed_rev,
-                            node->changed_date,
-                            node->changed_author));
+                              : NULL));

+  if (present && node->repos_relpath)
+    {
+      SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, node->changed_rev));
+      SVN_ERR(svn_sqlite__bind_int64(stmt, 11, node->changed_date));
+      SVN_ERR(svn_sqlite__bind_text(stmt, 12, node->changed_author));
+    }
+
   if (node->repos_relpath
       && node->presence != svn_wc__db_status_base_deleted)
     {
@@ -1504,13 +1508,17 @@
       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
                                         scratch_pool));

-      if (node->recorded_size != SVN_INVALID_FILESIZE)
-        SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
+      if (node->repos_relpath)
+        {
+          if (node->recorded_size != SVN_INVALID_FILESIZE)
+            SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));

-      SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time));
+          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time));
+        }
     }

-  if (node->properties && present) /* ### Never set, props done later */
+   /* ### Never set, props done later */
+  if (node->properties && present && node->repos_relpath)
     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
                                         scratch_pool));


------------------------------------------------------------------------
r1660659 | rhuijben | 2015-02-18 15:45:24 +0000 (Wed, 18 Feb 2015) | 24 lines
Changed paths:
   M /subversion/trunk/subversion/include/private/svn_sqlite.h
   M /subversion/trunk/subversion/libsvn_subr/sqlite.c
   M /subversion/trunk/subversion/libsvn_wc/wc_db_util.c
   M /subversion/trunk/subversion/tests/libsvn_subr/sqlite-test.c

Following up on r1660610 and r1660641, just get rid of the pool handling
inside the sqlite function callbacks. Our current users don't need it and
if needed by somebody else it can be handled via the baton.

* subversion/include/private/svn_sqlite.h
  (svn_sqlite__func_t): Replace scratch_pool argument with baton.

* subversion/libsvn_subr/sqlite.c
  (function_wrapper_baton_t): Remove pools.
  (wrapped_func): Just pass the actual sqlite3 values to the inner
    function.
  (clear_sqlite_function_scratch,
   clear_sqlite_function_state,
   clear_sqlite_function_scratch): Remove.
  (svn_sqlite__create_scalar_function): Remove pool registrations.
  (svn_sqlite__value_type,
   svn_sqlite__value_text): Assume other argument type.

* subversion/libsvn_wc/wc_db_util.c
  (relpath_depth_sqlite): Change argument.

* subversion/tests/libsvn_subr/sqlite-test.c
  (error_second): Change argument.


Index: subversion/libsvn_subr/sqlite.c
===================================================================
--- subversion/libsvn_subr/sqlite.c (revision 1660658)
+++ subversion/libsvn_subr/sqlite.c (revision 1660659)
@@ -1477,9 +1477,6 @@
 {
   svn_sqlite__func_t func;
   void *baton;
-
-  apr_pool_t *scratch_pool;
-  apr_pool_t *state_pool;
 };

 static void
@@ -1489,23 +1486,13 @@
 {
   struct function_wrapper_baton_t *fwb = sqlite3_user_data(context);
   svn_sqlite__context_t sctx;
-  svn_sqlite__value_t **local_vals =
-                            apr_palloc(fwb->scratch_pool,
-                                       sizeof(svn_sqlite__value_t *) * argc);
   svn_error_t *err;
-  int i;
+  void *void_values = values;

   sctx.context = context;

-  for (i = 0; i < argc; i++)
-    {
-      local_vals[i] = apr_palloc(fwb->scratch_pool, sizeof(*local_vals[i]));
-      local_vals[i]->value = values[i];
-    }
+  err = fwb->func(&sctx, argc, void_values, fwb->baton);

-  err = fwb->func(&sctx, argc, local_vals, fwb->scratch_pool);
-  svn_pool_clear(fwb->scratch_pool);
-
   if (err)
     {
       char buf[256];
@@ -1516,35 +1503,7 @@
     }
 }

-/* Forward definition */
-static apr_status_t clear_sqlite_function_scratch(void *baton);

-/* pool cleanup function for function context on state pool */
-static apr_status_t
-clear_sqlite_function_state(void *baton)
-{
-  struct function_wrapper_baton_t *fwb = baton;
-
-  svn_pool_destroy(fwb->scratch_pool);
-
-  apr_pool_cleanup_kill(fwb->scratch_pool, baton,
-                        clear_sqlite_function_scratch);
-
-  return APR_SUCCESS;
-}
-
-/* pool cleanup function for global cleanup on internal pool */
-static apr_status_t
-clear_sqlite_function_scratch(void *baton)
-{
-  struct function_wrapper_baton_t *fwb = baton;
-
-  apr_pool_cleanup_kill(fwb->state_pool, baton,
-                        clear_sqlite_function_state);
-
-  return APR_SUCCESS;
-}
-
 svn_error_t *
 svn_sqlite__create_scalar_function(svn_sqlite__db_t *db,
                                    const char *func_name,
@@ -1564,8 +1523,6 @@
      We create a subpool in the global pool and only destroy it
      when we want it to be destroyed */

-  fwb->state_pool = db->state_pool;
-  fwb->scratch_pool = svn_pool_create(NULL);
   fwb->func = func;
   fwb->baton = baton;

@@ -1577,13 +1534,6 @@
                                      fwb, wrapped_func, NULL, NULL),
              db);

-  apr_pool_cleanup_register(fwb->state_pool, fwb,
-                            clear_sqlite_function_state,
-                            apr_pool_cleanup_null);
-  apr_pool_cleanup_register(fwb->scratch_pool, fwb,
-                            clear_sqlite_function_scratch,
-                            apr_pool_cleanup_null);
-
   return SVN_NO_ERROR;
 }

@@ -1590,13 +1540,15 @@
 int
 svn_sqlite__value_type(svn_sqlite__value_t *val)
 {
-  return sqlite3_value_type(val->value);
+  void *v = val;
+  return sqlite3_value_type(v);
 }

 const char *
 svn_sqlite__value_text(svn_sqlite__value_t *val)
 {
-  return (const char *) sqlite3_value_text(val->value);
+  void *v = val;
+  return (const char *) sqlite3_value_text(v);
 }

 void
Index: subversion/tests/libsvn_subr/sqlite-test.c
===================================================================
--- subversion/tests/libsvn_subr/sqlite-test.c  (revision 1660658)
+++ subversion/tests/libsvn_subr/sqlite-test.c  (revision 1660659)
@@ -49,7 +49,7 @@
 error_second(svn_sqlite__context_t *sctx,
              int argc,
              svn_sqlite__value_t *values[],
-             apr_pool_t *scratch_pool)
+             void *baton)
 {
   static int i = 0;

Index: subversion/include/private/svn_sqlite.h
===================================================================
--- subversion/include/private/svn_sqlite.h (revision 1660658)
+++ subversion/include/private/svn_sqlite.h (revision 1660659)
@@ -63,7 +63,7 @@
 typedef svn_error_t *(*svn_sqlite__func_t)(svn_sqlite__context_t *sctx,
                                            int argc,
                                            svn_sqlite__value_t *values[],
-                                           apr_pool_t *scatch_pool);
+                                           void *baton);


 /* Step the given statement; if it returns SQLITE_DONE, reset the statement.
Index: subversion/libsvn_wc/wc_db_util.c
===================================================================
--- subversion/libsvn_wc/wc_db_util.c   (revision 1660658)
+++ subversion/libsvn_wc/wc_db_util.c   (revision 1660659)
@@ -83,7 +83,7 @@
 relpath_depth_sqlite(svn_sqlite__context_t *sctx,
                      int argc,
                      svn_sqlite__value_t *values[],
-                     apr_pool_t *scratch_pool)
+                     void *baton)
 {
   const char *path = NULL;
   apr_int64_t depth;

------------------------------------------------------------------------
r1660660 | rhuijben | 2015-02-18 15:52:13 +0000 (Wed, 18 Feb 2015) | 8 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/entries.c

Fix bind offset in upgrade query.
(Somehow sqlite uses 0 based columns for getting, but 1 based columns for
 binding... and I didn't see the difference in the db as the values were
 there:()

* subversion/libsvn_wc/entries.c
  (insert_node): Following up on r1660652, fix column offset.


Index: subversion/libsvn_wc/entries.c
===================================================================
--- subversion/libsvn_wc/entries.c  (revision 1660659)
+++ subversion/libsvn_wc/entries.c  (revision 1660660)
@@ -1471,9 +1471,9 @@

   if (present && node->repos_relpath)
     {
-      SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, node->changed_rev));
-      SVN_ERR(svn_sqlite__bind_int64(stmt, 11, node->changed_date));
-      SVN_ERR(svn_sqlite__bind_text(stmt, 12, node->changed_author));
+      SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, node->changed_rev));
+      SVN_ERR(svn_sqlite__bind_int64(stmt, 12, node->changed_date));
+      SVN_ERR(svn_sqlite__bind_text(stmt, 13, node->changed_author));
     }

   if (node->repos_relpath

------------------------------------------------------------------------
r1660671 | rhuijben | 2015-02-18 16:45:38 +0000 (Wed, 18 Feb 2015) | 10 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql
   M /subversion/trunk/subversion/tests/libsvn_wc/entries-compat.c

More manual wc-db test scenario fixes: Make the entries-compat base-set
follow the rules of a valid wc-ng database.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Add another verification.

* subversion/tests/libsvn_wc/entries-compat.c
  (TESTING_DATA): Fix op-depths. Add missing row. Use base-deleted for
    actual deletes, not 'not-present'.


Index: subversion/tests/libsvn_wc/entries-compat.c
===================================================================
--- subversion/tests/libsvn_wc/entries-compat.c (revision 1660670)
+++ subversion/tests/libsvn_wc/entries-compat.c (revision 1660671)
@@ -88,10 +88,6 @@

    "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "'); "

-   /* ### The file_externals column in NODES is temporary, and will be
-      ### removed.  However, to keep the tests passing, we need to add it
-      ### to the following insert statements.  *Be sure to remove it*. */
-
    /* load the base nodes into the nodes table */
   "insert into nodes values ("
   "  1, '', 0, null, 1, '', 1, 'normal',"
@@ -186,43 +182,43 @@
   "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-a', 1, 'J', null, null, null, 'normal',"
+  "  1, 'J/J-a', 2, 'J', null, null, null, 'normal',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-b', 1, 'J', 2, 'some/dir', 2, 'normal',"
+  "  1, 'J/J-b', 2, 'J', 2, 'some/dir', 2, 'normal',"
   "  null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-b/J-b-a', 1, 'J/J-b', 2, 'another/dir', 2, 'normal',"
+  "  1, 'J/J-b/J-b-a', 3, 'J/J-b', 2, 'another/dir', 2, 'normal',"
   "  null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-b/J-b-b', 1, 'J/J-b', null, null, null, 'normal',"
+  "  1, 'J/J-b/J-b-b', 3, 'J/J-b', null, null, null, 'normal',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-c', 1, 'J', null, null, null, 'not-present',"
+  "  1, 'J/J-c', 2, 'J', null, null, null, 'base-deleted',"
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-c/J-c-a', 1, 'J/J-c', null, null, null, 'not-present',"
+  "  1, 'J/J-c/J-c-a', 2, 'J/J-c', null, null, null, 'base-deleted',"
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-d', 1, 'J', 2, 'moved/file', 2, 'normal',"
+  "  1, 'J/J-d', 2, 'J', 2, 'moved/file', 2, 'normal',"
   "  1, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  10, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e', 1, 'J', null, null, null, 'not-present',"
+  "  1, 'J/J-e', 1, 'J', null, null, null, 'base-deleted',"
   "  null, 'other/place', 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'not-present',"
+  "  1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'base-deleted',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'not-present',"
+  "  1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'base-deleted',"
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
@@ -230,7 +226,11 @@
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-f', 1, 'J', null, null, null, 'normal',"
+  "  1, 'J/J-f', 1, 'J', null, null, null, 'base-deleted',"
+  "  null, null, 'dir', null, null, null, null, null, null, null,"
+  "  null, null, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'J/J-f', 2, 'J', null, null, null, 'normal',"
   "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
@@ -254,11 +254,11 @@
   "  null, null, 'dir', null, 'immediates', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'L/L-a', 1, 'L', null, null, null, 'not-present',"
+  "  1, 'L/L-a', 1, 'L', null, null, null, 'base-deleted',"
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'not-present',"
+  "  1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'base-deleted',"
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
    "insert into actual_node values ("
Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660670)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660671)
@@ -189,3 +189,10 @@
         OR n.repos_path !=
            RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath)
         OR n.revision != p.revision)
+
+UNION all
+
+SELECT n.local_relpath, n.op_depth, 'SV010: Invalid op-root presence'
+FROM nodes n
+WHERE n.op_depth = relpath_depth(local_relpath)
+  AND presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE, MAP_BASE_DELETED)

------------------------------------------------------------------------
r1660687 | rhuijben | 2015-02-18 17:37:25 +0000 (Wed, 18 Feb 2015) | 8 lines
Changed paths:
   M /subversion/trunk/subversion/tests/libsvn_wc/db-test.c

Further improvements to the db-test test setup: fix shadowing
of 'J'.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Shadow node at the right op-depth.
  (test_scan_deletion): Use SVN_TEST_STRING_ASSERT. Revert recent
    assumption change.


Index: subversion/tests/libsvn_wc/db-test.c
===================================================================
--- subversion/tests/libsvn_wc/db-test.c    (revision 1660686)
+++ subversion/tests/libsvn_wc/db-test.c    (revision 1660687)
@@ -262,7 +262,7 @@
   "  null, null, 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
-  "  1, 'J/J-e/J-e-b/Jeba', 2, 'J/J-e/J-e-b', null, null, null, 'base-deleted',"
+  "  1, 'J/J-e/J-e-b/Jeba', 1, 'J/J-e/J-e-b', null, null, null, 'base-deleted',"
   "  null, null, 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
@@ -1207,8 +1207,7 @@
                                    base_del_abspath, pool));
   SVN_ERR(validate_abspath(local_abspath, "other/place/J-e-b/Jeba",
                                    moved_to_abspath, pool));
-  SVN_ERR(validate_abspath(local_abspath, "J/J-e",
-                                   work_del_abspath, pool));
+  SVN_TEST_STRING_ASSERT(work_del_abspath, NULL);

   /* Base-deleted tree extending past added WORKING tree.  */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1221,8 +1220,8 @@
   /* Implicit delete of "J" (via replacement).  */
   SVN_ERR(validate_abspath(local_abspath, "J",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(work_del_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL);
+  SVN_TEST_STRING_ASSERT(work_del_abspath, NULL);

   /* Root of delete. Parent is a BASE node. */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1234,8 +1233,8 @@
             pool, pool));
   SVN_ERR(validate_abspath(local_abspath, "K",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(work_del_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL);
+  SVN_TEST_STRING_ASSERT(work_del_abspath, NULL);

   /* Base-deleted tree. Start below root.  */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1247,8 +1246,8 @@
             pool, pool));
   SVN_ERR(validate_abspath(local_abspath, "K",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(moved_to_abspath == NULL);
-  SVN_TEST_ASSERT(work_del_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL);
+  SVN_TEST_STRING_ASSERT(work_del_abspath, NULL);

   /* Base-deleted tree via move.  */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1264,7 +1263,7 @@
                                    moved_to_abspath, pool));
   SVN_ERR(validate_abspath(local_abspath, "moved/away",
                                    copy_op_root_abspath, pool));
-  SVN_TEST_ASSERT(work_del_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(work_del_abspath, NULL);

   /* Subtree deletion of added tree. Start at child.  */
   SVN_ERR(svn_wc__db_scan_deletion(
@@ -1274,8 +1273,8 @@
             NULL,
             db, svn_dirent_join(local_abspath, "L/L-a/L-a-a", pool),
             pool, pool));
-  SVN_TEST_ASSERT(base_del_abspath == NULL);
-  SVN_TEST_ASSERT(moved_to_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(base_del_abspath, NULL);
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL);
   SVN_ERR(validate_abspath(local_abspath, "L/L-a",
                                    work_del_abspath, pool));

@@ -1287,8 +1286,8 @@
             NULL,
             db, svn_dirent_join(local_abspath, "L/L-a", pool),
             pool, pool));
-  SVN_TEST_ASSERT(base_del_abspath == NULL);
-  SVN_TEST_ASSERT(moved_to_abspath == NULL);
+  SVN_TEST_STRING_ASSERT(base_del_abspath, NULL);
+  SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL);
   SVN_ERR(validate_abspath(local_abspath, "L/L-a",
                                    work_del_abspath, pool));


------------------------------------------------------------------------
r1660742 | rhuijben | 2015-02-18 22:15:50 +0000 (Wed, 18 Feb 2015) | 37 lines
Changed paths:
   M /subversion/trunk/build/transform_sql.py
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql
   M /subversion/trunk/subversion/libsvn_wc/wc-queries.sql
   M /subversion/trunk/subversion/libsvn_wc/wc_db.c
   M /subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py
   M /subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c

Fix svn_wc__db_op_make_copy_internal()'s handling of mixed revision BASE trees.

This fixes some issues around tree conflict handling, and improves
database consistency.

* build/transform_sql.py
  (process_file): Allow passing columns and binding variables as argument of
    IS_STRICT_DESCENDANT_OF, like the other macros.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Add documentation to the older statements. Extend.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_DELETE_WORKING_BASE_DELETE): Split into non-recursive...
  (STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE): ... and recursive variants.
  (STMT_INSERT_WORKING_NODE_FROM_BASE_COPY): Allow replacing nodes, and
    handle moved_to when doing that.

* subversion/libsvn_wc/wc_db.c
  (db_base_remove): Update caller.
  (make_copy_txn): Add recursion arguments to determine when a new op-depth
    is needed. Simplify code, by moving the initial shadowing to the calling
    function.
  (make_copy_move_moved_to): New function.
  (svn_wc__db_op_make_copy_internal): Update caller. Shadow all nodes with
    base-deleted before calling make_copy_txn. Move moved-to information
    to this layer.

* subversion/tests/cmdline/tree_conflict_tests.py
  (update_delete_mixed_rev): Remove XFail. Tweak status value.

* subversion/tests/libsvn_wc/op-depth-test.c
  (make_copy_mixed): New function. Tests svn_wc__db_op_make_copy_internal.
   make_copy_and_delete_mixed): New functions. Tests svn_wc__db_base_remove
     and shows an existing issue with move handling on base-delete.
  (test_list): Add new items.


Index: subversion/libsvn_wc/wc-queries.sql
===================================================================
--- subversion/libsvn_wc/wc-queries.sql (revision 1660741)
+++ subversion/libsvn_wc/wc-queries.sql (revision 1660742)
@@ -233,6 +233,16 @@

 -- STMT_DELETE_WORKING_BASE_DELETE
 DELETE FROM nodes
+WHERE wc_id = ?1 AND local_relpath = ?2
+  AND presence = MAP_BASE_DELETED
+  AND op_depth > ?3
+  AND op_depth = (SELECT MIN(op_depth) FROM nodes n
+                    WHERE n.wc_id = ?1
+                      AND n.local_relpath = nodes.local_relpath
+                      AND op_depth > ?3)
+
+-- STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE
+DELETE FROM nodes
 WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
   AND presence = MAP_BASE_DELETED
   AND op_depth > ?3
@@ -1023,15 +1033,17 @@
 ORDER BY local_relpath

 -- STMT_INSERT_WORKING_NODE_FROM_BASE_COPY
-INSERT INTO nodes (
+INSERT OR REPLACE INTO nodes (
     wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
     revision, presence, depth, kind, changed_revision, changed_date,
     changed_author, checksum, properties, translated_size, last_mod_time,
-    symlink_target )
+    symlink_target, moved_to )
 SELECT wc_id, local_relpath, ?3 /*op_depth*/, parent_relpath, repos_id,
     repos_path, revision, presence, depth, kind, changed_revision,
     changed_date, changed_author, checksum, properties, translated_size,
-    last_mod_time, symlink_target
+    last_mod_time, symlink_target,
+    (SELECT moved_to FROM nodes
+     WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to
 FROM nodes
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0

Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660741)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660742)
@@ -76,6 +76,8 @@
 END;

 -- STMT_STATIC_VERIFY
+/* A parent node must exist for every normal node except the root.
+   That node must exist at a lower or equal op-depth */
 SELECT local_relpath, op_depth, 'SV001: No ancestor in NODES'
 FROM nodes n WHERE local_relpath != ''
  AND file_external IS NULL
@@ -86,6 +88,8 @@

 UNION ALL

+/* All ACTUAL nodes must have an equivalent NODE in NODES
+   or be only one level deep (delete-delete tc) */
 SELECT local_relpath, -1, 'SV002: No ancestor in ACTUAL'
 FROM actual_node a WHERE local_relpath != ''
  AND NOT EXISTS(SELECT 1 from nodes i
@@ -96,7 +100,8 @@
                   AND i.local_relpath=a.local_relpath)

 UNION ALL
-
+/* Verify if the ACTUAL data makes sense for the related node.
+   Only conflict data is valid if there is none */
 SELECT a.local_relpath, -1, 'SV003: Bad or Unneeded actual data'
 FROM actual_node a
 LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath
@@ -111,7 +116,8 @@
                   AND i.local_relpath=a.parent_relpath)

 UNION ALL
-
+/* If a node is not present in the working copy (normal, add, copy) it doesn't
+   have revision details stored on this record */
 SELECT local_relpath, op_depth, 'SV004: Unneeded node data'
 FROM nodes
 WHERE presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)
@@ -129,7 +135,8 @@
      OR inherited_props IS NOT NULL)

 UNION ALL
-
+/* base-deleted nodes don't have a repository location. They are just
+   shadowing without a replacement */
 SELECT local_relpath, op_depth, 'SV005: Unneeded base-deleted node data'
 FROM nodes
 WHERE presence IN (MAP_BASE_DELETED)
@@ -138,7 +145,7 @@
      OR revision IS NOT NULL)

 UNION ALL
-
+/* Verify if type specific data is set (or not set for wrong type) */
 SELECT local_relpath, op_depth, 'SV006: Kind specific data invalid on normal'
 FROM nodes
 WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
@@ -157,7 +164,8 @@
                                       ELSE symlink_target IS NOT NULL END))

 UNION ALL
-
+/* Local-adds are always their own operation (read: they don't have
+   op-depth descendants, nor are op-depth descendants */
 SELECT local_relpath, op_depth, 'SV007: Invalid op-depth for local add'
 FROM nodes
 WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE)
@@ -165,7 +173,9 @@
   AND op_depth != relpath_depth(local_relpath)

 UNION ALL
-
+/* op-depth descendants are only valid if they have a direct parent
+   node at the same op-depth. Only certain types allow further
+   descendants */
 SELECT local_relpath, op_depth, 'SV008: Node missing ancestor'
 FROM nodes n
 WHERE op_depth < relpath_depth(local_relpath)
@@ -174,11 +184,12 @@
                  WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath
                    AND p.op_depth=n.op_depth
                    AND (p.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
-                        OR (p.presence = MAP_BASE_DELETED
+                        OR (p.presence IN (MAP_BASE_DELETED, MAP_NOT_PRESENT)
                             AND n.presence = MAP_BASE_DELETED)))

-UNION all
-
+UNION ALL
+/* Present op-depth descendants have the repository location implied by their
+   ancestor */
 SELECT n.local_relpath, n.op_depth, 'SV009: Copied descendant mismatch'
 FROM nodes n
 JOIN nodes p
@@ -188,11 +199,65 @@
    AND (n.repos_id != p.repos_id
         OR n.repos_path !=
            RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath)
-        OR n.revision != p.revision)
+        OR n.revision != p.revision
+        OR p.kind != MAP_DIR
+        OR n.moved_here != p.moved_here)

-UNION all
-
+UNION ALL
+/* Only certain presence values are valid as op-root.
+   Note that the wc-root always has presence normal or incomplete */
 SELECT n.local_relpath, n.op_depth, 'SV010: Invalid op-root presence'
 FROM nodes n
 WHERE n.op_depth = relpath_depth(local_relpath)
   AND presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE, MAP_BASE_DELETED)
+
+UNION ALL
+/* If a node is shadowed, all its present op-depth descendants
+   must be shadowed at the same op-depth as well */
+SELECT n.local_relpath, n.op_depth, 'SV011: Incomplete shadowing'
+FROM nodes n
+JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath
+ AND s.op_depth = relpath_depth(s.local_relpath)
+ AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d
+                   WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath
+                     AND d.op_depth > n.op_depth)
+WHERE n.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+  AND EXISTS(SELECT 1
+             FROM nodes dn
+             WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth
+               AND dn.presence IN (MAP_NORMAL, MAP_INCOMPLETE)
+               AND IS_STRICT_DESCENDANT_OF(dn.local_relpath, n.local_relpath)
+               AND dn.file_external IS NULL
+               AND NOT EXISTS(SELECT 1
+                              FROM nodes ds
+                              WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth
+                                AND ds.local_relpath=dn.local_relpath))
+
+UNION ALL
+/* A base-delete is only valid if it directly deletes a present node */
+SELECT s.local_relpath, s.op_depth, 'SV012: Invalid base-delete'
+FROM nodes s
+LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath
+ AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d
+                   WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath
+                     AND d.op_depth < s.op_depth)
+WHERE s.presence = MAP_BASE_DELETED
+  AND n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)
+
+UNION ALL
+
+SELECT d.local_relpath, d.op_depth, 'SV013: Moved here without origin'
+FROM nodes d
+WHERE d.op_depth = relpath_depth(d.local_relpath)
+  AND d.moved_here = 1
+  AND NOT EXISTS(SELECT 1 FROM nodes s
+                 WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath)
+
+UNION ALL
+
+SELECT s.local_relpath, s.op_depth, 'SV014: Moved to without target'
+FROM nodes s
+WHERE s.moved_to IS NOT NULL
+  AND NOT EXISTS(SELECT 1 FROM nodes d
+                 WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to
+                   AND d.moved_here =1 AND d.repos_path IS NOT NULL)
\ No newline at end of file
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c    (revision 1660741)
+++ subversion/libsvn_wc/wc_db.c    (revision 1660742)
@@ -2410,8 +2410,9 @@
     }
   if (keep_working)
     {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_WORKING_BASE_DELETE));
+      SVN_ERR(svn_sqlite__get_statement(
+                    &stmt, wcroot->sdb,
+                    STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
@@ -15010,95 +15011,163 @@
 static svn_error_t *
 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
               const char *local_relpath,
-              int op_depth,
+              apr_int64_t last_repos_id,
+              const char *last_repos_relpath,
+              svn_revnum_t last_revision,
+              int last_op_depth,
+              svn_boolean_t shadowed,
               apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-  svn_boolean_t add_working_base_deleted = FALSE;
-  svn_boolean_t remove_working = FALSE;
-  const apr_array_header_t *children;
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  int i;
+  svn_boolean_t have_row = FALSE;
+  svn_revnum_t revision;
+  apr_int64_t repos_id;
+  const char *repos_relpath;
+  svn_node_kind_t kind;
+  int op_depth = relpath_depth(local_relpath);

-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_LOWEST_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-  if (have_row)
+  if (last_op_depth != op_depth)
     {
-      svn_wc__db_status_t working_status;
-      int working_op_depth;
-
-      working_status = svn_sqlite__column_token(stmt, 1, presence_map);
-      working_op_depth = svn_sqlite__column_int(stmt, 0);
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_DEPTH_NODE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                                op_depth));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
       SVN_ERR(svn_sqlite__reset(stmt));
+      if (have_row)
+        shadowed = TRUE;
+    }

-      SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
-                     || working_status == svn_wc__db_status_base_deleted
-                     || working_status == svn_wc__db_status_not_present
-                     || working_status == svn_wc__db_status_incomplete);
+  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
+                                            &repos_relpath, &repos_id, NULL,
+                                            NULL, NULL, NULL, NULL, NULL, NULL,
+                                            NULL, NULL, NULL,
+                                            wcroot, local_relpath,
+                                            scratch_pool, scratch_pool));

-      /* Only change nodes in the layers where we are creating the copy.
-         Deletes in higher layers will just apply to the copy */
-      if (working_op_depth <= op_depth)
-        {
-          add_working_base_deleted = TRUE;
+  if (last_repos_relpath
+      && repos_id == last_repos_id
+      && revision == last_revision)
+    {
+      const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
+                                                   repos_relpath);

-          if (working_status == svn_wc__db_status_base_deleted)
-            remove_working = TRUE;
-        }
+      if (strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
+        op_depth = last_op_depth;
     }
-  else
-    SVN_ERR(svn_sqlite__reset(stmt));

-  if (remove_working)
+  /* Insert a not-present node to mark that we don't know what exists
+     here */
+  if (last_op_depth > 0 && last_op_depth != op_depth)
     {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_LOWEST_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
+      insert_working_baton_t iwb;
+
+      blank_iwb(&iwb);
+      iwb.presence = svn_wc__db_status_not_present;
+      iwb.op_depth = last_op_depth;
+
+      iwb.original_repos_id = repos_id;
+      iwb.original_repos_relpath = repos_relpath;
+      iwb.original_revnum = revision;
+      iwb.kind = kind;
+
+      SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
     }

-  if (add_working_base_deleted)
+  /* Can we add a new copy node at the wanted op-depth? */
+  if (!have_row || op_depth == last_op_depth)
     {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_INSERT_DELETE_FROM_BASE));
+      int i;
+
+      SVN_ERR(svn_sqlite__get_statement(
+                    &stmt, wcroot->sdb,
+                    STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
                                 op_depth));
       SVN_ERR(svn_sqlite__step_done(stmt));
+
+      if (shadowed)
+        SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
+                                        op_depth, scratch_pool));
+
+      if (kind == svn_node_dir)
+        {
+          const apr_array_header_t *children;
+          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+          SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
+                                        0, scratch_pool, iterpool));
+
+          for (i = 0; i < children->nelts; i++)
+            {
+              const char *name = APR_ARRAY_IDX(children, i, const char *);
+              const char *copy_relpath;
+
+              svn_pool_clear(iterpool);
+
+              copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
+
+              SVN_ERR(make_copy_txn(wcroot, copy_relpath,
+                                    repos_id, repos_relpath, revision,
+                                    op_depth, shadowed, scratch_pool));
+            }
+          svn_pool_destroy(iterpool);
+        }
     }
   else
     {
+      /* Auch... we can't make a copy of whatever comes deeper, as this
+         op-depth is already filled by something else. Let's hope
+         the user doesn't mind.
+
+         Luckily we know that the moves are already moved to the shadowing
+         layer, so we can just remove dangling base-deletes if there are
+         any.
+       */
+      /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
+
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                      STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
+                    STMT_DELETE_WORKING_BASE_DELETE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
-                                op_depth));
+                                last_op_depth));
       SVN_ERR(svn_sqlite__step_done(stmt));
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                    STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                                last_op_depth));
+      SVN_ERR(svn_sqlite__step_done(stmt));
     }

-  /* Get the BASE children, as WORKING children don't need modifications */
-  SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
-                               0, scratch_pool, iterpool));
+  return SVN_NO_ERROR;
+}

-  for (i = 0; i < children->nelts; i++)
-    {
-      const char *name = APR_ARRAY_IDX(children, i, const char *);
-      const char *copy_relpath;
+/* Helper for svn_wc__db_op_make_copy_internal */
+static svn_error_t *
+make_copy_move_moved_to(svn_wc__db_wcroot_t *wcroot,
+                        const char *local_relpath,
+                        int cur_op_depth,
+                        int new_op_depth,
+                        const char *moved_to,
+                        apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;

-      svn_pool_clear(iterpool);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_MOVED_TO_RELPATH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                            local_relpath, cur_op_depth));
+  SVN_ERR(svn_sqlite__step_done(stmt));

-      copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_MOVED_TO_RELPATH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
+                            local_relpath, new_op_depth, moved_to));
+  SVN_ERR(svn_sqlite__step_done(stmt));

-      SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, iterpool));
-    }
-
-  svn_pool_destroy(iterpool);
-
   return SVN_NO_ERROR;
 }

+
 svn_error_t *
 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
                                  const char *local_relpath,
@@ -15140,12 +15209,56 @@
     }
   else
     {
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+      svn_error_t *err = NULL;
+      int affected_rows;
+
+      op_depth = relpath_depth(local_relpath);
       /* We don't allow copies to contain server-excluded nodes;
          the update editor is going to have to bail out. */
-      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
+      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, iterpool));

+      /* Insert a shadowing layer */
+      SVN_ERR(svn_sqlite__get_statement(
+                        &stmt, wcroot->sdb,
+                        STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
+
+      /* As we are keeping whatever is below, move the*/
+
+      SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
+                                wcroot->wc_id, local_relpath,
+                                0, op_depth));
+      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+      SVN_ERR_ASSERT(affected_rows > 0);
+
+      SVN_ERR(svn_sqlite__get_statement(
+                        &stmt, wcroot->sdb,
+                        STMT_SELECT_MOVED_DESCENDANTS_SRC));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
+                                wcroot->wc_id, local_relpath,
+                                op_depth));
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      while (have_row && !err)
+        {
+          err = make_copy_move_moved_to(
+                              wcroot,
+                              svn_sqlite__column_text(stmt, 1, iterpool),
+                              svn_sqlite__column_int(stmt, 0),
+                              op_depth,
+                              svn_sqlite__column_text(stmt, 4, iterpool),
+                              iterpool);
+
+          SVN_ERR(svn_sqlite__step(&have_row, stmt));
+        }
+
+      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+
+
       SVN_ERR(make_copy_txn(wcroot, local_relpath,
-                            relpath_depth(local_relpath), scratch_pool));
+                            INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
+                            relpath_depth(local_relpath), FALSE, scratch_pool));
     }

   if (conflicts)
Index: subversion/tests/libsvn_wc/op-depth-test.c
===================================================================
--- subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660741)
+++ subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660742)
@@ -10993,6 +10993,334 @@

   return SVN_NO_ERROR;
 }
+
+static svn_error_t *
+make_copy_mixed(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "make_copy_mixed", opts, pool));
+
+  SVN_ERR(sbox_wc_mkdir(&b, "A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/O"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T"));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_update(&b, "", 1));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r2", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r3", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r4", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r5", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(sbox_wc_update(&b, "", 5));
+  SVN_ERR(sbox_wc_update(&b, "A", 4));
+  SVN_ERR(sbox_wc_update(&b, "A/B", 3));
+  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
+  SVN_ERR(sbox_wc_update(&b, "A/B/K", 1));
+  SVN_ERR(sbox_wc_update(&b, "A/N/O", 3));
+
+  SVN_ERR(sbox_wc_delete(&b, "A/B/C/F"));
+  SVN_ERR(sbox_wc_delete(&b, "A/B/G/J"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
+
+  SVN_ERR(sbox_wc_update(&b, "A/N/P", 1));
+  SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1));
+  SVN_ERR(sbox_wc_delete(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q"));
+  SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H"));
+
+  /* And something that can't be represented */
+  SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1));
+  SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
+      {0, "A",            "normal",       4, "A"},
+      {0, "A/B",          "normal",       3, "A/B"},
+      {0, "A/B/C",        "normal",       2, "A/B/C"},
+      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
+      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
+      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
+      {0, "A/B/G",        "normal",       3, "A/B/G"},
+      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
+      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
+      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
+      {0, "A/B/K",        "normal",       1, "A/B/K"},
+      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
+      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
+      {0, "A/N",          "normal",       4, "A/N"},
+      {0, "A/N/O",        "normal",       3, "A/N/O"},
+      {0, "A/N/P",        "normal",       1, "A/N/P"},
+      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
+      {0, "A/R",          "normal",       4, "A/R"},
+      {0, "A/R/S",        "normal",       4, "A/R/S"},
+      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
+      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
+      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
+      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
+      {3, "A/N/P",        "normal",       NO_COPY_FROM},
+      {3, "A/N/Q",        "base-deleted", NO_COPY_FROM, "Q"},
+      {4, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
+      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
+      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},
+
+      {0}
+    };
+
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"),
+                                  NULL, NULL, pool));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
+      {0, "A",            "normal",       4, "A"},
+      {0, "A/B",          "normal",       3, "A/B"},
+      {0, "A/B/C",        "normal",       2, "A/B/C"},
+      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
+      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
+      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
+      {0, "A/B/G",        "normal",       3, "A/B/G"},
+      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
+      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
+      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
+      {0, "A/B/K",        "normal",       1, "A/B/K"},
+      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
+      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
+      {0, "A/N",          "normal",       4, "A/N"},
+      {0, "A/N/O",        "normal",       3, "A/N/O"},
+      {0, "A/N/P",        "normal",       1, "A/N/P"},
+      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
+      {0, "A/R",          "normal",       4, "A/R"},
+      {0, "A/R/S",        "normal",       4, "A/R/S"},
+      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
+      {1, "A",            "normal",       4, "A"},
+      {1, "A/B",          "not-present",  3, "A/B"},
+      {1, "A/B/C",        "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C/D",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
+      {1, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/G",        "base-deleted", NO_COPY_FROM},
+      {1, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
+      {1, "A/B/G/I",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/G/J",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/K",        "base-deleted", NO_COPY_FROM},
+      {1, "A/B/K/L",      "base-deleted", NO_COPY_FROM},
+      {1, "A/B/K/M",      "base-deleted", NO_COPY_FROM},
+      {1, "A/N",          "normal",       4, "A/N"},
+      {1, "A/N/O",        "not-present",  3, "A/N/O"},
+      {1, "A/N/P",        "not-present",  1, "A/N/P"},
+      {1, "A/N/Q",        "not-present",  1, "A/N/Q", FALSE, "Q"},
+      {1, "A/R",          "normal",       4, "A/R"},
+      {1, "A/R/S",        "normal",       4, "A/R/S"},
+      {1, "A/R/S/T",      "normal",       4, "A/R/S/T"},
+      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
+      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
+      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
+      {2, "A/B",          "normal",       3, "A/B"},
+      {2, "A/B/C",        "not-present",  2, "A/B/C"},
+      {2, "A/B/G",        "normal",       3, "A/B/G"},
+      {2, "A/B/G/H",      "normal",       3, "A/B/G/H"},
+      {2, "A/B/G/I",      "normal",       3, "A/B/G/I"},
+      {2, "A/B/G/J",      "normal",       3, "A/B/G/J"},
+      {2, "A/B/K",        "not-present",  1, "A/B/K"},
+      {3, "A/B/C",        "normal",       2, "A/B/C"},
+      {3, "A/B/C/D",      "normal",       2, "A/B/C/D"},
+      {3, "A/B/C/E",      "not-present",  1, "A/B/C/E"},
+      {3, "A/B/C/F",      "normal",       2, "A/B/C/F"},
+      {3, "A/B/K",        "normal",       1, "A/B/K"},
+      {3, "A/B/K/L",      "normal",       1, "A/B/K/L"},
+      {3, "A/B/K/M",      "normal",       1, "A/B/K/M"},
+      {3, "A/N/O",        "normal",       3, "A/N/O"},
+      {3, "A/N/P",        "normal",       NO_COPY_FROM},
+      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},
+
+      {0}
+    };
+
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+make_copy_and_delete_mixed(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "make_copy_and_del_mixed", opts, pool));
+
+  SVN_ERR(sbox_wc_mkdir(&b, "A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/E"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/H"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/I"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/L"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/K/M"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/O"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/Q"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/R/S/T"));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_update(&b, "", 1));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r2", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r3", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r4", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+  SVN_ERR(sbox_wc_propset(&b, "k", "r5", ""));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(sbox_wc_update(&b, "", 5));
+  SVN_ERR(sbox_wc_update(&b, "A", 4));
+  SVN_ERR(sbox_wc_update(&b, "A/B", 3));
+  SVN_ERR(sbox_wc_update(&b, "A/B/C", 2));
+  SVN_ERR(sbox_wc_update(&b, "A/B/K", 1));
+  SVN_ERR(sbox_wc_update(&b, "A/N/O", 3));
+
+  SVN_ERR(sbox_wc_delete(&b, "A/B/C/F"));
+  SVN_ERR(sbox_wc_delete(&b, "A/B/G/J"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B/G/J"));
+
+  SVN_ERR(sbox_wc_update(&b, "A/N/P", 1));
+  SVN_ERR(sbox_wc_update(&b, "A/N/Q", 1));
+  SVN_ERR(sbox_wc_delete(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/N/P"));
+  SVN_ERR(sbox_wc_move(&b, "A/N/Q", "Q"));
+  SVN_ERR(sbox_wc_move(&b, "A/B/G/H", "H"));
+
+  /* And something that can't be represented */
+  SVN_ERR(sbox_wc_update(&b, "A/B/C/E", 1));
+  SVN_ERR(sbox_wc_move(&b, "A/B/C/E", "E"));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
+      {0, "A",            "normal",       4, "A"},
+      {0, "A/B",          "normal",       3, "A/B"},
+      {0, "A/B/C",        "normal",       2, "A/B/C"},
+      {0, "A/B/C/D",      "normal",       2, "A/B/C/D"},
+      {0, "A/B/C/E",      "normal",       1, "A/B/C/E"},
+      {0, "A/B/C/F",      "normal",       2, "A/B/C/F"},
+      {0, "A/B/G",        "normal",       3, "A/B/G"},
+      {0, "A/B/G/H",      "normal",       3, "A/B/G/H"},
+      {0, "A/B/G/I",      "normal",       3, "A/B/G/I"},
+      {0, "A/B/G/J",      "normal",       3, "A/B/G/J"},
+      {0, "A/B/K",        "normal",       1, "A/B/K"},
+      {0, "A/B/K/L",      "normal",       1, "A/B/K/L"},
+      {0, "A/B/K/M",      "normal",       1, "A/B/K/M"},
+      {0, "A/N",          "normal",       4, "A/N"},
+      {0, "A/N/O",        "normal",       3, "A/N/O"},
+      {0, "A/N/P",        "normal",       1, "A/N/P"},
+      {0, "A/N/Q",        "normal",       1, "A/N/Q"},
+      {0, "A/R",          "normal",       4, "A/R"},
+      {0, "A/R/S",        "normal",       4, "A/R/S"},
+      {0, "A/R/S/T",      "normal",       4, "A/R/S/T"},
+      {1, "E",            "normal",       1, "A/B/C/E", MOVED_HERE},
+      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
+      {1, "Q",            "normal",       1, "A/N/Q", MOVED_HERE},
+      {3, "A/N/P",        "normal",       NO_COPY_FROM},
+      {3, "A/N/Q",        "base-deleted", NO_COPY_FROM, "Q"},
+      {4, "A/B/C/E",      "base-deleted", NO_COPY_FROM, "E"},
+      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM, "H"},
+      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},
+
+      {0}
+    };
+
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(svn_wc__db_base_remove(b.wc_ctx->db, sbox_wc_path(&b, "A"),
+                                 TRUE, FALSE, FALSE, 99,
+                                 NULL, NULL, pool));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",             "normal",       5, "", NOT_MOVED, "k"},
+      {0, "A",            "not-present",  99, "A"},
+      {1, "A",            "normal",       4, "A"},
+      {1, "A/B",          "not-present",  3, "A/B"},
+      {1, "A/N",          "normal",       4, "A/N"},
+      {1, "A/N/O",        "not-present",  3, "A/N/O"},
+      {1, "A/N/P",        "not-present",  1, "A/N/P"},
+      {1, "A/N/Q",        "not-present",  1, "A/N/Q", FALSE},
+      {1, "A/R",          "normal",       4, "A/R"},
+      {1, "A/R/S",        "normal",       4, "A/R/S"},
+      {1, "A/R/S/T",      "normal",       4, "A/R/S/T"},
+      {1, "E",            "normal",       1, "A/B/C/E"},
+      {1, "H",            "normal",       3, "A/B/G/H", MOVED_HERE},
+      {1, "Q",            "normal",       1, "A/N/Q"},
+      {2, "A/B",          "normal",       3, "A/B"},
+      {2, "A/B/C",        "not-present",  2, "A/B/C"},
+      {2, "A/B/G",        "normal",       3, "A/B/G"},
+      {2, "A/B/G/H",      "normal",       3, "A/B/G/H"},
+      {2, "A/B/G/I",      "normal",       3, "A/B/G/I"},
+      {2, "A/B/G/J",      "normal",       3, "A/B/G/J"},
+      {3, "A/B/C",        "normal",       2, "A/B/C"},
+      {3, "A/B/C/D",      "normal",       2, "A/B/C/D"},
+      {3, "A/B/C/E",      "not-present",  1, "A/B/C/E"},
+      {3, "A/B/C/F",      "normal",       2, "A/B/C/F"},
+      {2, "A/B/K",        "not-present",  1, "A/B/K"},
+      {3, "A/B/K",        "normal",       1, "A/B/K"},
+      {3, "A/B/K/L",      "normal",       1, "A/B/K/L"},
+      {3, "A/B/K/M",      "normal",       1, "A/B/K/M"},
+      {3, "A/N/O",        "normal",       3, "A/N/O"},
+      {3, "A/N/P",        "normal",       NO_COPY_FROM},
+      {4, "A/B/C/F",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/H",      "base-deleted", NO_COPY_FROM},
+      {4, "A/B/G/J",      "normal",       NO_COPY_FROM},
+
+      {0}
+    };
+
+    /* This currently fails because Q and E are still marked as moved,
+       while there is nothing to be moved. */
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  return SVN_NO_ERROR;
+}
+
 /* ---------------------------------------------------------------------- */
 /* The list of test functions */

@@ -11202,6 +11530,10 @@
                        "move edit obstruction"),
     SVN_TEST_OPTS_PASS(move_deep_bump,
                        "move deep bump"),
+    SVN_TEST_OPTS_PASS(make_copy_mixed,
+                       "make a copy of a mixed revision tree"),
+    SVN_TEST_OPTS_XFAIL(make_copy_and_delete_mixed,
+                       "make a copy of a mixed revision tree and del"),
     SVN_TEST_NULL
   };

Index: subversion/tests/cmdline/tree_conflict_tests.py
===================================================================
--- subversion/tests/cmdline/tree_conflict_tests.py (revision 1660741)
+++ subversion/tests/cmdline/tree_conflict_tests.py (revision 1660742)
@@ -1449,7 +1449,6 @@
   run_and_verify_svn(None, [],
                      'ci', '-m', '', wc_dir)

-@XFail()
 def update_delete_mixed_rev(sbox):
   "update that deletes mixed-rev"

@@ -1484,6 +1483,10 @@
                         status='A ', copied='+', treeconflict='C', wc_rev='-')
   expected_status.tweak('A/B/F', 'A/B/E', 'A/B/E/beta', 'A/B/lambda',
                         copied='+', wc_rev='-')
+
+  # The entries world doesn't see a changed revision as another add
+  # while the WC-NG world does...
+  expected_status.tweak('A/B/E', status='A ', entry_status='  ')
   run_and_verify_update(wc_dir,
                         expected_output, expected_disk, expected_status,
                         None, None, None, None, None, 1,
Index: build/transform_sql.py
===================================================================
--- build/transform_sql.py  (revision 1660741)
+++ build/transform_sql.py  (revision 1660742)
@@ -140,7 +140,7 @@

       # '/'+1 == '0'
       line = re.sub(
-            r'IS_STRICT_DESCENDANT_OF[(]([A-Za-z_.]+), ([?][0-9]+)[)]',
+            r'IS_STRICT_DESCENDANT_OF[(]([?]?[A-Za-z0-9_.]+), ([?]?[A-Za-z0-9_.]+)[)]',
             r"(((\1) > (CASE (\2) WHEN '' THEN '' ELSE (\2) || '/' END))" +
             r" AND ((\1) < CASE (\2) WHEN '' THEN X'FFFF' ELSE (\2) || '0' END))",
             line)

------------------------------------------------------------------------
r1660758 | rhuijben | 2015-02-18 22:49:12 +0000 (Wed, 18 Feb 2015) | 15 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-queries.sql
   M /subversion/trunk/subversion/libsvn_wc/wc_db.c
   M /subversion/trunk/subversion/tests/libsvn_wc/db-test.c

Bring the wc-db tests in the world where we store moves in WORKING, and
fix the db query that allowed this test to survive so long.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_SELECT_DELETION_INFO): Rewrite as join to allow access to more
    columns. Add moved_to result.
  (STMT_SELECT_DELETION_INFO_SCAN): Remove known bad statement.

* subversion/libsvn_wc/wc_db.c
  (scan_deletion_txn): Always use STMT_SELECT_DELETION_INFO.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Stop storing moves in the pre 1.8.x locations,
    move them to WORKING instead.


Index: subversion/tests/libsvn_wc/db-test.c
===================================================================
--- subversion/tests/libsvn_wc/db-test.c    (revision 1660757)
+++ subversion/tests/libsvn_wc/db-test.c    (revision 1660758)
@@ -140,7 +140,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal',"
-  "  null, 'other/place', 'dir', null, 'infinity', null, null, null, null, null,"
+  "  null, null, 'dir', null, 'infinity', null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal',"
@@ -172,7 +172,7 @@
   "  15, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-b', 0, 'K', 1, 'K/K-b', 1, 'normal',"
-  "  null, 'moved/away', 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  15, null, null, null, null);"
   ""
    /* Load data into NODES table;
@@ -251,7 +251,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 2, 'J', null, null, null, 'base-deleted',"
-  "  null, null, 'dir', null, null, null, null, null, null, null,"
+  "  null, 'other/place', 'dir', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 2, 'J/J-e', null, null, null, 'base-deleted',"
@@ -283,7 +283,7 @@
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-b', 1, 'K', null, null, null, 'base-deleted',"
-  "  null, null, 'file', null, null, null, null, null, null, null,"
+  "  null, 'moved/away', 'file', null, null, null, null, null, null, null,"
   "  null, null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L', 1, '', 2, 'from', 2, 'normal',"
Index: subversion/libsvn_wc/wc-queries.sql
===================================================================
--- subversion/libsvn_wc/wc-queries.sql (revision 1660757)
+++ subversion/libsvn_wc/wc-queries.sql (revision 1660758)
@@ -462,27 +462,15 @@
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0

 -- STMT_SELECT_DELETION_INFO
-SELECT (SELECT b.presence FROM nodes AS b
-         WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0),
-       work.presence, work.op_depth
-FROM nodes_current AS work
-WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0
+SELECT b.presence, w.presence, w.op_depth, w.moved_to
+FROM nodes w
+LEFT JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0
+WHERE w.wc_id = ?1 AND w.local_relpath = ?2
+  AND w.op_depth = (SELECT MAX(op_depth) FROM nodes d
+                    WHERE d.wc_id = ?1 AND d.local_relpath = ?2
+                      AND d.op_depth > 0)
 LIMIT 1

--- STMT_SELECT_DELETION_INFO_SCAN
-/* ### FIXME.  moved.moved_to IS NOT NULL works when there is
- only one move but we need something else when there are several. */
-SELECT (SELECT b.presence FROM nodes AS b
-         WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0),
-       work.presence, work.op_depth, moved.moved_to
-FROM nodes_current AS work
-LEFT OUTER JOIN nodes AS moved
-  ON moved.wc_id = work.wc_id
- AND moved.local_relpath = work.local_relpath
- AND moved.moved_to IS NOT NULL
-WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0
-LIMIT 1
-
 -- STMT_SELECT_MOVED_TO_NODE
 SELECT op_depth, moved_to
 FROM nodes
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c    (revision 1660757)
+++ subversion/libsvn_wc/wc_db.c    (revision 1660758)
@@ -4044,9 +4044,7 @@
   scan = (moved_to_op_root_relpath || moved_to_relpath);

   SVN_ERR(svn_sqlite__get_statement(
-                    &stmt, wcroot->sdb,
-                    scan ? STMT_SELECT_DELETION_INFO_SCAN
-                         : STMT_SELECT_DELETION_INFO));
+                    &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));

   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));

------------------------------------------------------------------------
r1660769 | rhuijben | 2015-02-18 23:47:20 +0000 (Wed, 18 Feb 2015) | 9 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/copy.c

Keep wc.db's move tracking stable when a 'svn mv' fails to rename the
target.

* subversion/libsvn_wc/copy.c
  (remove_all_conflict_markers): Add cancel support.
  (svn_wc__move2): Call svn_wc__db_op_delete on the already created
    target when the wc move operation failed. This keeps the moves
    state as before the operation.


Index: subversion/libsvn_wc/copy.c
===================================================================
--- subversion/libsvn_wc/copy.c (revision 1660768)
+++ subversion/libsvn_wc/copy.c (revision 1660769)
@@ -997,6 +997,8 @@
 remove_all_conflict_markers(svn_wc__db_t *db,
                             const char *src_dir_abspath,
                             const char *dst_dir_abspath,
+                            svn_cancel_func_t cancel_func,
+                            void *cancel_baton,
                             apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -1020,6 +1022,9 @@
       const char *name = apr_hash_this_key(hi);
       struct svn_wc__db_info_t *info = apr_hash_this_val(hi);

+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
+
       if (info->conflicted)
         {
           svn_pool_clear(iterpool);
@@ -1036,6 +1041,7 @@
                             db,
                             svn_dirent_join(src_dir_abspath, name, iterpool),
                             svn_dirent_join(dst_dir_abspath, name, iterpool),
+                            cancel_func, cancel_baton,
                             iterpool));
         }
     }
@@ -1093,8 +1099,26 @@
      is still in a valid state. So be careful when switching this over
      to the workqueue. */
   if (!metadata_only)
-    SVN_ERR(svn_io_file_rename(src_abspath, dst_abspath, scratch_pool));
+    {
+      svn_error_t *err;

+      err = svn_error_trace(svn_io_file_rename(src_abspath, dst_abspath,
+                                               scratch_pool));
+
+      /* Let's try if we can keep wc.db consistent even when the move
+         fails. Deleting the target is a wc.db only operation, while
+         going forward (delaying the error) would try to change
+         conflict markers, which might also fail. */
+      if (err)
+        return svn_error_trace(
+          svn_error_compose_create(
+              err,
+              svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE,
+                                   NULL, NULL, cancel_func, cancel_baton,
+                                   NULL, NULL,
+                                   scratch_pool)));
+    }
+
   SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                NULL, NULL, NULL, NULL, NULL, NULL,
@@ -1105,6 +1129,7 @@

   if (kind == svn_node_dir)
     SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath,
+                                        cancel_func, cancel_baton,
                                         scratch_pool));

   if (conflicted)

------------------------------------------------------------------------
r1660775 | rhuijben | 2015-02-19 00:22:03 +0000 (Thu, 19 Feb 2015) | 5 lines
Changed paths:
   M /subversion/trunk/subversion/tests/cmdline/move_tests.py

Following up on r1660769, also commit the patch to the relevant test.

* subversion/tests/cmdline/move_tests.py
  (move_missing): Update expected results.


Index: subversion/tests/cmdline/move_tests.py
===================================================================
--- subversion/tests/cmdline/move_tests.py  (revision 1660774)
+++ subversion/tests/cmdline/move_tests.py  (revision 1660775)
@@ -1156,23 +1156,11 @@
   expected_status.tweak('A/D/G', 'A/D/G/tau', 'A/D/G/pi', 'A/D/G/rho',
                         status='! ', entry_status='  ')

-  expected_status.add({
-    'R'                 : Item(status='! ', wc_rev='-',
-                               entry_status='A ', entry_copied='+'),
-    'R/pi'              : Item(status='! ', wc_rev='-',
-                               entry_status='  ', entry_copied='+'),
-    'R/tau'             : Item(status='! ', wc_rev='-',
-                               entry_status='  ', entry_copied='+'),
-    'R/rho'             : Item(status='! ', wc_rev='-',
-                               entry_status='  ', entry_copied='+'),
-  })
-
   # Verify that the status processing doesn't crash
   svntest.actions.run_and_verify_status(wc_dir, expected_status)

   # The issue is a crash when the destination is present
   os.mkdir(sbox.ospath('R'))
-  expected_status.tweak('R', status='A ', copied='+')

   svntest.actions.run_and_verify_status(wc_dir, expected_status)


------------------------------------------------------------------------
r1660781 | rhuijben | 2015-02-19 02:24:02 +0000 (Thu, 19 Feb 2015) | 19 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-queries.sql
   M /subversion/trunk/subversion/libsvn_wc/wc_db.c
   M /subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c

Make sure a depth limited commit that touches a move, but doesn't
commit it doesn't break move recording by moving some parts into
BASE (creating invalid state) and discarding other parts.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_COMMIT_DESCENDANTS_TO_BASE): Clear moved_to information.

* subversion/libsvn_wc/wc_db.c
  (moved_descendant_collect): New function extracted from...
  (moved_descendant_commit): ... this. Update recursive call.
  (commit_node): Collect moves that are about to be removed,
    and properly remove their recording if necessary.
    Stop removing locks recursively, let libsvn_client handle
    this properly.

* subversion/tests/libsvn_wc/op-depth-test.c
  (commit_moved_away_descendant): Extend test to verify for
    bad recording that previously occurred.


Index: subversion/tests/libsvn_wc/op-depth-test.c
===================================================================
--- subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660780)
+++ subversion/tests/libsvn_wc/op-depth-test.c  (revision 1660781)
@@ -6917,6 +6917,31 @@
      shadowed, like in this case. The commit processing doesn't
      support this yet though*/

+   {
+    nodes_row_t nodes[] = {
+      {0, "",                 "normal",       0, ""},
+      {0, "A",                "normal",       1, "A"},
+      {0, "A/A",              "normal",       2, "A/A"},
+      {0, "A/A/A",            "normal",       2, "A/A/A"},
+      {0, "A/A/A/A",          "normal",       2, "A/A/A/A"},
+      {0, "A/A/A/A/A",        "normal",       2, "A/A/A/A/A"},
+      {0, "A/A/A/A/A/A",      "normal",       2, "A/A/A/A/A/A"},
+      {0, "A_copied",         "normal",       2, "A_copied"},
+      {0, "A_copied/A",       "normal",       2, "A_copied/A"},
+      {0, "A_copied/A/A",     "normal",       2, "A_copied/A/A"},
+      {0, "A_copied/A/A/A",   "normal",       2, "A_copied/A/A/A"},
+      {0, "A_copied/A/A/A/A", "normal",       2, "A_copied/A/A/A/A"},
+      {0, "A_copied/A/A/A/A/A","normal",       2, "A_copied/A/A/A/A/A"},
+      {0, "AAA_moved",        "normal",       2, "AAA_moved"},
+      {0, "AAA_moved/A",      "normal",       2, "AAA_moved/A"},
+      {0, "AAA_moved/A/A",    "normal",       2, "AAA_moved/A/A"},
+      {0, "AAA_moved/A/A/A",  "normal",       2, "AAA_moved/A/A/A"},
+
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
   return SVN_NO_ERROR;
 }

@@ -6939,10 +6964,63 @@
   SVN_ERR(sbox_wc_delete(&b, "A/A"));
   SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A"));

+  {
+    nodes_row_t nodes[] = {
+      {0, "",                   "normal",       0, ""},
+      {0, "A",                  "normal",       1, "A"},
+      {0, "A/A",                "normal",       1, "A/A"},
+      {0, "A/A/A",              "normal",       1, "A/A/A"},
+      {0, "A/A/A/A",            "normal",       1, "A/A/A/A"},
+      {0, "A/A/A/A/A",          "normal",       1, "A/A/A/A/A"},
+      {0, "A/A/A/A/A/A",        "normal",       1, "A/A/A/A/A/A"},
+      {1, "A_copied",           "normal",       1, "A"},
+      {1, "A_copied/A",         "normal",       1, "A/A"},
+      {1, "A_copied/A/A",       "normal",       1, "A/A/A"},
+      {1, "A_copied/A/A/A",     "normal",       1, "A/A/A/A"},
+      {1, "A_copied/A/A/A/A",   "normal",       1, "A/A/A/A/A"},
+      {1, "A_copied/A/A/A/A/A", "normal",       1, "A/A/A/A/A/A"},
+      {1, "AAA_moved",          "normal",       1, "A/A/A", MOVED_HERE},
+      {1, "AAA_moved/A",        "normal",       1, "A/A/A/A", MOVED_HERE},
+      {1, "AAA_moved/A/A",      "normal",       1, "A/A/A/A/A", MOVED_HERE},
+      {1, "AAA_moved/A/A/A",    "normal",       1, "A/A/A/A/A/A", MOVED_HERE},
+      {2, "A/A",                "normal",       1, "A/A"},
+      {2, "A/A/A",              "normal",       1, "A/A/A", FALSE, "AAA_moved"},
+      {2, "A/A/A/A",            "normal",       1, "A/A/A/A"},
+      {2, "A/A/A/A/A",          "normal",       1, "A/A/A/A/A"},
+      {2, "A/A/A/A/A/A",        "normal",       1, "A/A/A/A/A/A"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
   /* And now I want to make sure that I can't commit A, without also
      committing AAA_moved, as that would break the move*/
   SVN_ERR(sbox_wc_commit(&b, "A"));

+  {
+    nodes_row_t nodes[] = {
+      {0, "",                   "normal",       0, ""},
+      {0, "A",                  "normal",       1, "A"},
+      {0, "A/A",                "normal",       2, "A/A"},
+      {0, "A/A/A",              "normal",       2, "A/A/A"},
+      {0, "A/A/A/A",            "normal",       2, "A/A/A/A"},
+      {0, "A/A/A/A/A",          "normal",       2, "A/A/A/A/A"},
+      {0, "A/A/A/A/A/A",        "normal",       2, "A/A/A/A/A/A"},
+      {1, "A_copied",           "normal",       1, "A"},
+      {1, "A_copied/A",         "normal",       1, "A/A"},
+      {1, "A_copied/A/A",       "normal",       1, "A/A/A"},
+      {1, "A_copied/A/A/A",     "normal",       1, "A/A/A/A"},
+      {1, "A_copied/A/A/A/A",   "normal",       1, "A/A/A/A/A"},
+      {1, "A_copied/A/A/A/A/A", "normal",       1, "A/A/A/A/A/A"},
+      {1, "AAA_moved",          "normal",       1, "A/A/A"},
+      {1, "AAA_moved/A",        "normal",       1, "A/A/A/A"},
+      {1, "AAA_moved/A/A",      "normal",       1, "A/A/A/A/A"},
+      {1, "AAA_moved/A/A/A",    "normal",       1, "A/A/A/A/A/A"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
   return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
                           "The commit should have failed");

Index: subversion/libsvn_wc/wc-queries.sql
===================================================================
--- subversion/libsvn_wc/wc-queries.sql (revision 1660780)
+++ subversion/libsvn_wc/wc-queries.sql (revision 1660781)
@@ -388,6 +388,7 @@
                  revision = ?6,
                  dav_cache = NULL,
                  moved_here = NULL,
+                 moved_to = NULL,
                  presence = CASE presence
                               WHEN MAP_NORMAL THEN MAP_NORMAL
                               WHEN MAP_EXCLUDED THEN MAP_EXCLUDED
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c    (revision 1660780)
+++ subversion/libsvn_wc/wc_db.c    (revision 1660781)
@@ -11387,6 +11387,48 @@
   return SVN_NO_ERROR;
 }

+static svn_error_t *
+moved_descendant_collect(apr_hash_t **map,
+                        svn_wc__db_wcroot_t *wcroot,
+                        const char *local_relpath,
+                        int op_depth,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  *map = NULL;
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_MOVED_DESCENDANTS_SRC));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                                         local_relpath,
+                                         op_depth));
+
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (! have_row)
+    return svn_error_trace(svn_sqlite__reset(stmt));
+
+  /* Find all moved descendants. Key them on target, because that is
+     always unique */
+  while (have_row)
+    {
+      const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
+      const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
+
+      if (!*map)
+        *map = apr_hash_make(result_pool);
+
+      svn_hash_sets(*map, to_relpath, src_relpath);
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
 /* Helper for svn_wc__db_global_commit()

    Makes local_relpath and all its descendants at the same op-depth represent
@@ -11398,50 +11440,26 @@
    Assumptions:
      * local_relpath is not the working copy root (can't be moved)
      * repos_relpath is not the repository root (can't be moved)
-   */
+ */
 static svn_error_t *
 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
                         const char *local_relpath,
-                        int op_depth,
                         apr_int64_t repos_id,
                         const char *repos_relpath,
                         svn_revnum_t revision,
+                        apr_hash_t *children,
                         apr_pool_t *scratch_pool)
 {
-  apr_hash_t *children;
   apr_pool_t *iterpool;
   svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
   apr_hash_index_t *hi;

   SVN_ERR_ASSERT(*local_relpath != '\0'
                  && *repos_relpath != '\0');

-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_MOVED_DESCENDANTS_SRC));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
-                                         local_relpath,
-                                         op_depth));
+  if (!children)
+    return SVN_NO_ERROR;

-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  if (! have_row)
-    return svn_error_trace(svn_sqlite__reset(stmt));
-
-  children = apr_hash_make(scratch_pool);
-
-  /* First, obtain all moved descendants */
-  /* To keep error handling simple, first cache them in a hashtable */
-  while (have_row)
-    {
-      const char *src_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
-      const char *to_relpath = svn_sqlite__column_text(stmt, 4, scratch_pool);
-
-      svn_hash_sets(children, src_relpath, to_relpath);
-
-      SVN_ERR(svn_sqlite__step(&have_row, stmt));
-    }
-  SVN_ERR(svn_sqlite__reset(stmt));
-
   /* Then update them */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_COMMIT_UPDATE_ORIGIN));
@@ -11449,11 +11467,12 @@
   iterpool = svn_pool_create(scratch_pool);
   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
     {
-      const char *src_relpath = apr_hash_this_key(hi);
-      const char *to_relpath = apr_hash_this_val(hi);
+      const char *src_relpath = apr_hash_this_val(hi);
+      const char *to_relpath = apr_hash_this_key(hi);
       const char *new_repos_relpath;
       int to_op_depth = relpath_depth(to_relpath);
       int affected;
+      apr_hash_t *map;

       svn_pool_clear(iterpool);

@@ -11480,9 +11499,11 @@
       SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
 #endif

-      SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
+      SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
+                                       iterpool, iterpool));
+      SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
                                       repos_id, new_repos_relpath, revision,
-                                      iterpool));
+                                      map, iterpool));
     }

   svn_pool_destroy(iterpool);
@@ -11563,6 +11584,7 @@
   const char *repos_relpath;
   int op_depth;
   svn_wc__db_status_t old_presence;
+  svn_boolean_t moved_here;

     /* If we are adding a file or directory, then we need to get
      repository information from the parent node since "this node" does
@@ -11631,6 +11653,8 @@

   old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);

+  moved_here = svn_sqlite__column_int(stmt_info, 15);
+
   /* ### other stuff?  */

   SVN_ERR(svn_sqlite__reset(stmt_info));
@@ -11639,7 +11663,14 @@
   if (op_depth > 0)
     {
       int affected_rows;
+      apr_hash_t *map_pre = NULL;
+      apr_hash_t *map_post = NULL;
+      svn_boolean_t op_root = (relpath_depth(local_relpath) == op_depth);

+      /* First collect the moves that we might delete in a bit */
+      SVN_ERR(moved_descendant_collect(&map_pre, wcroot, local_relpath, 0,
+                                       scratch_pool, scratch_pool));
+
       /* This removes all layers of this node and at the same time determines
          if we need to remove shadowed layers below our descendants. */

@@ -11672,26 +11703,61 @@
          be integrated, they really affect a different op-depth and
          completely different nodes (via a different recursion pattern). */

-      /* Collapse descendants of the current op_depth in layer 0 */
-      SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
-                                repos_id, repos_relpath, new_revision,
-                                scratch_pool));
+      if (op_root)
+        {
+          /* Collapse descendants of the current op_depth to layer 0,
+             this includes moved-from/to clearing */
+          SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
+                                    repos_id, repos_relpath, new_revision,
+                                    scratch_pool));
+        }

+      SVN_ERR(moved_descendant_collect(&map_post, wcroot, local_relpath, 0,
+                                       scratch_pool, scratch_pool));
+
+
       /* And make the recorded local moves represent moves of the node we just
          committed. */
-      SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
+      SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
                                       repos_id, repos_relpath, new_revision,
-                                      scratch_pool));
+                                      map_post, scratch_pool));

-      /* This node is no longer modified, so no node was moved here */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_CLEAR_MOVED_TO_FROM_DEST));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
-                                            local_relpath));
+      if (map_pre && map_post != map_pre)
+        {
+          apr_hash_index_t *hi;

-      SVN_ERR(svn_sqlite__step_done(stmt));
+          for (hi = apr_hash_first(scratch_pool, map_pre);
+               hi; hi = apr_hash_next(hi))
+            {
+              const char *to_relpath = apr_hash_this_key(hi);
+              svn_error_t *err;
+
+              if (map_post && svn_hash_gets(map_post, to_relpath))
+                continue;
+
+              err = clear_moved_here(wcroot, to_relpath, scratch_pool);
+
+              if (err)
+                {
+                  if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+                    return svn_error_trace(err);
+
+                  svn_error_clear(err); /* Node already committed? */
+                }
+            }
+        }
+
+      if (op_root && moved_here)
+        {
+          /* This node is no longer modified, so no node was moved here */
+          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                            STMT_CLEAR_MOVED_TO_FROM_DEST));
+          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                                                local_relpath));
+
+          SVN_ERR(svn_sqlite__step_done(stmt));
+        }
     }
-
   /* Update or add the BASE_NODE row with all the new information.  */

   if (*local_relpath == '\0')
@@ -11775,7 +11841,7 @@
       svn_sqlite__stmt_t *lock_stmt;

       SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
-                                        STMT_DELETE_LOCK_RECURSIVELY));
+                                        STMT_DELETE_LOCK));
       SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
       SVN_ERR(svn_sqlite__step_done(lock_stmt));
     }

------------------------------------------------------------------------
r1660782 | rhuijben | 2015-02-19 02:29:15 +0000 (Thu, 19 Feb 2015) | 9 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/wc-checks.sql

Checkpoint extensions to the verification statements.

Currently all tests except db-tests, entries-compatibility, one
op-depth test and 2 upgrade tests keep the database valid according
to this set of checks.

* subversion/libsvn_wc/wc-checks.sql
  (STMT_STATIC_VERIFY): Extend verifications.


Index: subversion/libsvn_wc/wc-checks.sql
===================================================================
--- subversion/libsvn_wc/wc-checks.sql  (revision 1660781)
+++ subversion/libsvn_wc/wc-checks.sql  (revision 1660782)
@@ -245,7 +245,7 @@
   AND n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE)

 UNION ALL
-
+/* If moved_here is set on an op-root, there must be a proper moved_to */
 SELECT d.local_relpath, d.op_depth, 'SV013: Moved here without origin'
 FROM nodes d
 WHERE d.op_depth = relpath_depth(d.local_relpath)
@@ -254,10 +254,19 @@
                  WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath)

 UNION ALL
-
+/* If moved_to is set there should be an moved op root at the target */
 SELECT s.local_relpath, s.op_depth, 'SV014: Moved to without target'
 FROM nodes s
 WHERE s.moved_to IS NOT NULL
   AND NOT EXISTS(SELECT 1 FROM nodes d
                  WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to
-                   AND d.moved_here =1 AND d.repos_path IS NOT NULL)
\ No newline at end of file
+                   AND d.op_depth = relpath_depth(d.local_relpath)
+                   AND d.moved_here =1 AND d.repos_path IS NOT NULL)
+
+UNION ALL
+/* Moves are stored in the working layers, not in BASE */
+SELECT n.local_relpath, n.op_depth, 'SV015: Invalid data for BASE'
+FROM nodes n
+WHERE n.op_depth = 0
+  AND (n.moved_to IS NOT NULL
+       OR n.moved_here IS NOT NULL)

------------------------------------------------------------------------
r1660818 | rhuijben | 2015-02-19 09:35:27 +0000 (Thu, 19 Feb 2015) | 5 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/entries.c

* subversion/libsvn_wc/entries.c
  (write_entry): When merging the difference between X and Y in a local
    addition, don't just drop the repository location of X on importing
    tree conflicts from the entries world.


Index: subversion/libsvn_wc/entries.c
===================================================================
--- subversion/libsvn_wc/entries.c  (revision 1660817)
+++ subversion/libsvn_wc/entries.c  (revision 1660818)
@@ -1912,11 +1912,6 @@

           WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);

-          /* Fix dubious data stored by old clients, local adds don't have
-             a repository URL. */
-          if (conflict->reason == svn_wc_conflict_reason_added)
-            conflict->src_left_version = NULL;
-
           SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
                                              scratch_pool, scratch_pool));


------------------------------------------------------------------------
r1660828 | rhuijben | 2015-02-19 10:17:27 +0000 (Thu, 19 Feb 2015) | 19 lines
Changed paths:
   M /subversion/trunk/subversion/libsvn_wc/entries.c
   M /subversion/trunk/subversion/tests/cmdline/depth_tests.py

Properly record upgrade copies of excluded nodes to the op-depth of
their parent node on upgrade.

This fixes a database inconsistency after upgrade_tests.py 22
"upgrade 1.6.x wc that has depth=exclude"
where an exclude was recorded as op-root.

I haven't tested this, but I think we would have tried to commit
the exclude as delete after upgrade without this patch.

* subversion/libsvn_wc/entries.c
  (write_entry): Handle excluded nodes as descendant instead of their
    own operation.

* subversion/tests/cmdline/depth_tests.py
  (commit_excluded): Add (unrelated) regression test on how an
    excluded directory should be handled over commits.
  (test_list): Add commit_excluded.


Index: subversion/tests/cmdline/depth_tests.py
===================================================================
--- subversion/tests/cmdline/depth_tests.py (revision 1660827)
+++ subversion/tests/cmdline/depth_tests.py (revision 1660828)
@@ -2886,7 +2886,83 @@
     # ra_neon added a spurious not-present row that does not show up in status
     raise svntest.Failure("count changed from '%s' to '%s'" % (val1, val2))

+def commit_excluded(sbox):
+  "commit an excluded node"

+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D/G' : Item(status='D '),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        None,
+                                        expected_status,
+                                        None, None, None, None, None, False,
+                                        "--set-depth=exclude",
+                                        sbox.ospath('A/D/G'))
+
+  sbox.simple_copy('A/D', 'D')
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'D' : Item(verb='Adding'),
+  })
+  
+  expected_status.add({
+    'D'          : Item(status='  ', wc_rev='2'),
+    'D/H'        : Item(status='  ', wc_rev='2'),
+    'D/H/chi'    : Item(status='  ', wc_rev='2'),
+    'D/H/psi'    : Item(status='  ', wc_rev='2'),
+    'D/H/omega'  : Item(status='  ', wc_rev='2'),
+    'D/gamma'    : Item(status='  ', wc_rev='2')
+  })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None, wc_dir)
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D/G'     : Item(status='A '),
+    'A/D/G/pi'  : Item(status='A '),
+    'A/D/G/tau' : Item(status='A '),
+    'A/D/G/rho' : Item(status='A '),
+    'D/G'       : Item(status='A '),
+    'D/G/pi'    : Item(status='A '),
+    'D/G/tau'   : Item(status='A '),
+    'D/G/rho'   : Item(status='A ')
+  })
+
+  expected_status.tweak(wc_rev=2)
+
+  expected_status.add({
+    'D'         : Item(status='  ', wc_rev='2'),
+    'D/G'       : Item(status='  ', wc_rev='2'),
+    'D/G/pi'    : Item(status='  ', wc_rev='2'),
+    'D/G/rho'   : Item(status='  ', wc_rev='2'),
+    'D/G/tau'   : Item(status='  ', wc_rev='2'),
+    'D/H'       : Item(status='  ', wc_rev='2'),
+    'D/H/chi'   : Item(status='  ', wc_rev='2'),
+    'D/H/psi'   : Item(status='  ', wc_rev='2'),
+    'D/H/omega' : Item(status='  ', wc_rev='2'),
+    'D/gamma'   : Item(status='  ', wc_rev='2'),
+    'A/D/G'     : Item(status='  ', wc_rev='2'),
+    'A/D/G/rho' : Item(status='  ', wc_rev='2'),
+    'A/D/G/tau' : Item(status='  ', wc_rev='2'),
+    'A/D/G/pi'  : Item(status='  ', wc_rev='2')
+  })
+
+  svntest.actions.run_and_verify_update(wc_dir,
+                                        expected_output,
+                                        None,
+                                        expected_status,
+                                        None, None, None, None, None, False,
+                                        "--set-depth=infinity", wc_dir)
+
 #----------------------------------------------------------------------
 # list all tests here, starting with None:
 test_list = [ None,
@@ -2937,6 +3013,7 @@
               commit_then_immediates_update,
               revert_depth_files,
               spurious_nodes_row,
+              commit_excluded,
               ]

 if __name__ == "__main__":
Index: subversion/libsvn_wc/entries.c
===================================================================
--- subversion/libsvn_wc/entries.c  (revision 1660827)
+++ subversion/libsvn_wc/entries.c  (revision 1660828)
@@ -2293,6 +2293,11 @@
         {
           working_node->op_depth = parent_node->work->op_depth;
         }
+      else if (working_node->presence == svn_wc__db_status_excluded
+               && parent_node->work)
+        {
+          working_node->op_depth = parent_node->work->op_depth;
+        }
       else if (!entry->copied)
         {
           working_node->op_depth

------------------------------------------------------------------------

@igilham
Copy link
Author

igilham commented Feb 19, 2015

I had to limit the size of the log to a day's worth. Any more and GitHub can't deal with it.

@lslewis901
Copy link

Just curious if you had reviewed the StatSVN (http://wiki.statsvn.org/) or svnstat tool (http://svnstat.sourceforge.net/)? The first one does heat maps and tracks changes by developer/date using the SVN diff and a SVN log file.

@adamtornhill
Copy link
Owner

Yes, I tested statsvn just last year. The heat map is nice and the churn graph gives an overview of the general trend. If you work with Subversion, statsvn is a good complement to Code Maat.
Btw, I won't support svn churn metrics in Code Maat. Instead I plan to open source one of my old Python scripts that does the same thing but in a more memory efficient way: fetch a range of revisions, run diff on each one of them, and calculate churn. I just need to clean-up the script.
Best regards,Adam-- 
Homepage: www.adamtornhill.com
Twitter: @adamtornhill

Your Code as a Crime Scene:
https://pragprog.com/book/atcrime/your-code-as-a-crime-scene
Lisp for the Web: 
https://leanpub.com/lispweb
Patterns in C:
https://leanpub.com/patternsinc

 Den torsdag, 7 maj 2015 19:24 skrev Lindsey Lewis <[email protected]>:

Just curious if you had reviewed the statsvn tool? It does heat maps and tracks changes by developer/date using the SVN diff and a SVN log file.—
Reply to this email directly or view it on GitHub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants