[PATCH] convert: svn-sink: apply property changes after adding dirs/files

Maxim Dounin mdounin at mdounin.ru
Thu Dec 27 06:16:00 CST 2007


Hello!

On Thu, Dec 27, 2007 at 03:28:29AM +0300, Maxim Dounin wrote:

> On Sat, 15 Dec 2007, Maxim Dounin wrote:
> 
> > When subversion used as a conversion sink, current code tries to set 
> > svn:executable property in putfile() hook. This may happen before apropriate 
> > file or dir was added to subversion, and so conversion will crash.
> >
> > Attached patch fixes it by moving actual work into putcommit().
> 
> Updated version of the patch. It also addresses similar issue with copy to 
> new directory.
> 
> Note: test-convert-svn-sink is expected to fail unless patch-rename.txt 
> from thread "[PATCH] convert: svn-sink: test if execute bit was actually 
> stored" also applied.

Sorry, looks like patch was mangled by my mail client. Resending it again as attachment.

Maxim Dounin
-------------- next part --------------
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1198714486 -10800
# Node ID a7da468d57897e7685c8c6ecb1b80bd20c0c151e
# Parent  1ce185f3c640a0b7210004e3d2145ef64e853a9c
convert: svn-sink: copy and set properties after adding dirs/files

We can't store properties for files we haven't added to repo. Similarly,
we can't copy file to directory we haven't added to svn yet. Remember
needed changes and apply them in putcommit().

diff -r 1ce185f3c640 -r a7da468d5789 hgext/convert/subversion.py
--- a/hgext/convert/subversion.py	Wed Dec 26 23:03:32 2007 +0100
+++ b/hgext/convert/subversion.py	Thu Dec 27 03:14:46 2007 +0300
@@ -725,6 +725,9 @@ class svn_sink(converter_sink, commandli
         converter_sink.__init__(self, ui, path)
         commandline.__init__(self, ui, 'svn')
         self.delete = []
+        self.setexec = []
+        self.delexec = []
+        self.copies = []
         self.wc = None
         self.cwd = os.getcwd()
 
@@ -792,15 +795,18 @@ class svn_sink(converter_sink, commandli
             util.set_exec(self.wjoin(filename), 'x' in flags)
             if was_exec:
                 if 'x' not in flags:
-                    self.run0('propdel', 'svn:executable', filename)
+                    self.delexec.append(filename)
             else:
                 if 'x' in flags:
-                    self.run0('propset', 'svn:executable', '*', filename)
-            
+                    self.setexec.append(filename)
+
     def delfile(self, name):
         self.delete.append(name)
 
     def copyfile(self, source, dest):
+        self.copies.append([source, dest])
+
+    def _copyfile(self, source, dest):
         # SVN's copy command pukes if the destination file exists, but
         # our copyfile method expects to record a copy that has
         # already occurred.  Cross the semantic gap.
@@ -831,15 +837,18 @@ class svn_sink(converter_sink, commandli
                 dirs.add(f[:i])
         return dirs
 
-    def add_files(self, files):
+    def add_dirs(self, files):
         add_dirs = [d for d in self.dirs_of(files)
                     if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
         if add_dirs:
             add_dirs.sort()
             self.run('add', non_recursive=True, quiet=True, *add_dirs)
+        return add_dirs
+
+    def add_files(self, files):
         if files:
             self.run('add', quiet=True, *files)
-        return files.union(add_dirs)
+        return files
         
     def tidy_dirs(self, names):
         dirs = list(self.dirs_of(names))
@@ -857,7 +866,7 @@ class svn_sink(converter_sink, commandli
 
     def revid(self, rev):
         return u"svn:%s@%s" % (self.uuid, rev)
-        
+
     def putcommit(self, files, parents, commit):
         for parent in parents:
             try:
@@ -865,12 +874,24 @@ class svn_sink(converter_sink, commandli
             except KeyError:
                 pass
         entries = set(self.delete)
+        files = util.frozenset(files)
+        entries.update(self.add_dirs(files.difference(entries)))
+        if self.copies:
+            for s, d in self.copies:
+                self._copyfile(s, d)
+            self.copies = []
         if self.delete:
             self.run0('delete', *self.delete)
             self.delete = []
-        files = util.frozenset(files)
         entries.update(self.add_files(files.difference(entries)))
         entries.update(self.tidy_dirs(entries))
+        if self.delexec:
+            self.run0('propdel', 'svn:executable', *self.delexec)
+            self.delexec = []
+        if self.setexec:
+            self.run0('propset', 'svn:executable', '*', *self.setexec)
+            self.setexec = []
+
         fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
         fp = os.fdopen(fd, 'w')
         fp.write(commit.desc)
diff -r 1ce185f3c640 -r a7da468d5789 tests/test-convert-svn-sink
--- a/tests/test-convert-svn-sink	Wed Dec 26 23:03:32 2007 +0100
+++ b/tests/test-convert-svn-sink	Thu Dec 27 03:14:46 2007 +0300
@@ -59,6 +59,29 @@ hg convert -d svn a
 (cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
 test -x a-hg-wc/c && echo executable || echo not executable
 
+echo % executable in new directory
+
+rm -rf a a-hg a-hg-wc
+hg init a
+
+mkdir a/d1
+echo a > a/d1/a
+chmod +x a/d1/a
+hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory'
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+test -x a-hg-wc/d1/a && echo executable || echo not executable
+
+echo % copy to new directory
+
+mkdir a/d2
+hg --cwd a cp d1/a d2/a
+hg --cwd a ci -d '1 0' -A -m 'copy file to new directory'
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+
 echo % branchy history
 
 hg init b
diff -r 1ce185f3c640 -r a7da468d5789 tests/test-convert-svn-sink.out
--- a/tests/test-convert-svn-sink.out	Wed Dec 26 23:03:32 2007 +0100
+++ b/tests/test-convert-svn-sink.out	Thu Dec 27 03:14:46 2007 +0300
@@ -195,6 +195,65 @@ At revision 5.
 </logentry>
 </log>
 executable
+% executable in new directory
+adding d1/a
+assuming destination a-hg
+initializing svn repo 'a-hg'
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 add executable file in new directory
+At revision 1.
+                1        1 test         .
+                1        1 test         d1
+                1        1 test         d1/a
+<?xml version="1.0"?>
+<log>
+<logentry
+   revision="1">
+<author>test</author>
+<date/>
+<paths>
+<path
+   action="A">/d1</path>
+<path
+   action="A">/d1/a</path>
+</paths>
+<msg>add executable file in new directory</msg>
+</logentry>
+</log>
+executable
+% copy to new directory
+assuming destination a-hg
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 copy file to new directory
+At revision 2.
+                2        2 test         .
+                2        1 test         d1
+                2        1 test         d1/a
+                2        2 test         d2
+                2        2 test         d2/a
+<?xml version="1.0"?>
+<log>
+<logentry
+   revision="2">
+<author>test</author>
+<date/>
+<paths>
+<path
+   action="A">/d2</path>
+<path
+   copyfrom-path="/d1/a"
+   copyfrom-rev="1"
+   action="A">/d2/a</path>
+</paths>
+<msg>copy file to new directory</msg>
+</logentry>
+</log>
 % branchy history
 adding b
 adding left-1


More information about the Mercurial-devel mailing list