Details
-
Bug
-
Status: Closed
-
Blocker
-
Resolution: Fixed
-
1.7.x
-
None
Description
Consider the following simple scenario, which used to work in svn 1.6.x, but doesn't work in 1.7.x: 1) create 'file.txt' and commit it to repository 'A/trunk' directory; 2) copy entire 'A/trunk' directory to 'A/tags/1.0' using 'svn copy URL URL' command; 3) create 'B/trunk' directory in the repository with one empty 'import' subdirectory, which will be used for importing 'A/tags/1.0/file.txt' via svn:externals; 4) get last revision, at which file.txt was modified via 'svn list A/tags/1.0/file.txt -v' command (say revision 1); 5) checkout 'B/trunk' into a working copy and set svn:externals property over 'import' subdirectory to: ' -r1 ^/A/tags/1.0/file.txt file.txt'; 6) finally, update 'B' working copy, which should fetch file.txt into 'import' WC directory; Step 6 of the above scenario silently fails in 1.7.x, i.e. subversion reports, that file.txt has been fetched at revision 1, but 'import' directory in WC remains empty. One may think, that this looks like desired behavior, because ^/A/tags/1.0/file.txt does not exist at revision 1 and therefore I should either use ' -r2 ^/A/tags/1.0/file.txt file.txt' or ' -r1 ^/A/trunk/file.txt file.txt' to get things done. But this is not the point here. I believe, that semantics of operative revision must be the same regardless of where it is used - either in svn commands or svn:externals definitions. This semantics is described in the Subversion Book "The Peg Revision Algorithm" section. Namely, it says: ------------------------------------------------------------------------------ The Subversion command-line client performs the peg revision algorithm any time it needs to resolve possible ambiguities in the paths and revisions provided to it. Here's an example of such an invocation: $ svn/|command|/ -r/|OPERATIVE-REV|/ item@/|PEG-REV|/ If /|OPERATIVE-REV|/ is older than /|PEG-REV|/, the algorithm is as follows: 1. Locate /|item|/ in the revision identified by /|PEG-REV|/. There can be only one such object. 2. Trace the object's history backwards (through any possible renames) to its ancestor in the revision /|OPERATIVE-REV|/. 3. Perform the requested action on that ancestor, wherever it is located, or whatever its name might be or might have been at that time. ------------------------------------------------------------------------------ Getting back to my example. When I run: svn cat -r 1 file:///`pwd`/repos/A/tags/1.0/file.txt or svn co -r 1 file:///`pwd`/repos/A/tags/1.0 tags-checkout subversion happily prints file.txt contents or downloads it to a working copy. This happens because it is able to cross copy command boundary. BTW, this works fine both in 1.6.x and 1.7.x. So, I expect, that svn:externals definition, which uses operative revision, should have identical behavior. And, in fact, it does in 1.6.x. Also note, that 1.7.x sort of has it. During WC update svn 1.7.x client provides the following output: Fetching external item into 'import/file.txt': External at revision 1. Which tells me, that file.txt has been successfully fetched - there are no error messages here. But there is no file.txt in WC after update either. So this looks like a definite bug to me. I've posted this issue to users mailing list: http://svn.haxx.se/users/archive-2012-05/0226.shtml I also had the following chat conversation with @stsp in order to confirm my point of view: ------------------------------------------------------------------------ [20:36] <alex_siyanko> hello, I'm looking for a "buddy" to confirm a bug in 1.7.x svn client. I posted bug report here: http://svn.haxx.se/users/archive-2012-05/0226.shtml but I'm not sure that I was heard and don't know what to do next. [20:45] <@stsp> alex_siyanko, i read that and i'm unsure about how we want externals to behave [20:45] <@stsp> but i see your point [20:45] <@stsp> if you like, you can file an issue that tracks further discussion [20:45] <@stsp> note that changing existing behavior might not be possible due to backwards compat rules [20:48] <alex_siyanko> well, in my last (3rd) email I think I explained it very clearly. I believe that when svn:externals rule is used with operative revision over a file, it must cross any copy/rename boundaries, as it used to be in 1.6.x and is described in the red book. [20:50] <alex_siyanko> in any case current behavior is kind of inconsistent - subversion neither responds with 'path not found' error nor it fetches the file into a WC [20:51] <@stsp> i agree and that is not the question [20:51] <@stsp> the question is whether changing the existing behavior would have any undesired side effects in existing setups [20:51] <@stsp> it's just something we need to consider [20:53] <@stsp> and yes, if 1.6.x used to do this then it is a regression of course [20:53] <@stsp> so please file an issue for this if you can, and quote me as your bug buddy [20:53] <@stsp> thanks! ------------------------------------------------------------------------ Here is the script to reproduce the described bug: ------------------------------------------------------------------------ #!/bin/sh # You might need to adjust these lines to point to your # compiled-from-source Subversion binaries, if using those: if [ -z "$SVN" ]; then SVN=`which svn` SVNADMIN=`which svnadmin` fi # Use English output. LC_ALL=C; export LC_ALL # Select an access method. If svn://, the svnserve setup is # handled automagically by this script; but if http://, then # you'll have to configure it yourself first. # # URL=http://localhost/SOMETHING/repos # URL=svn://localhost/repos URL=file:///`pwd`/repos ${SVNADMIN} create repos echo "### Making a Greek Tree for import..." mkdir import-me mkdir import-me/A mkdir import-me/A/trunk mkdir import-me/A/tags mkdir import-me/B mkdir import-me/B/trunk mkdir import-me/B/trunk/import echo "This is the file 'file.txt'" > import-me/A/trunk/file.txt echo "### Done." echo "" echo "### Importing it..." (cd import-me; ${SVN} import -q -m "Initial import." ${URL}) echo "### Done." echo "" echo "### copy A/trunk to A/tags/1.0 ###" ${SVN} cp ${URL}/A/trunk ${URL}/A/tags/1.0 -m 'make 1.0 release of project A' revision=`${SVN} ls ${URL}/A/tags/1.0/file.txt -v| grep -P -o '^\s+\d+' | grep -P -o '\d+'` echo "### 'A/tags/1.0/file.txt' Last Commit Revision: $revision ###" echo "### checkout B/trunk to wc ###" ${SVN} co ${URL}/B/trunk wc cd wc echo "### set svn:externals property over B/trunk/import to: ' -r$revision ^/A/tags/1.0/file.txt file.txt' ###" ${SVN} propset svn:externals " -r$revision ^/A/tags/1.0/file.txt file.txt" import echo "### update B/trunk working copy ###" ${SVN} update echo "### list updated B/trunk wc contents ###" ls -R ----------------------------------------------------------------------------- And here the output from the above script: ----------------------------------------------------------------------------- ### Making a Greek Tree for import... ### Done. ### Importing it... ### Done. ### copy A/trunk to A/tags/1.0 ### Committed revision 2. ### 'A/tags/1.0/file.txt' Last Commit Revision: 1 ### ### checkout B/trunk to wc ### A wc/import Checked out revision 2. ### set svn:externals property over B/trunk/import to: ' -r1 ^/A/tags/1.0/file.txt file.txt' ### property 'svn:externals' set on 'import' ### update B/trunk working copy ### Updating '.': Fetching external item into 'import/file.txt': External at revision 1. At revision 2. ### list updated B/trunk wc contents ### .: import ./import: ----------------------------------------------------------------------------- Sincerely, Alex Siyanko
Original issue reported by alex_siyanko