hey,
 Here's a patch that adds symlink support (and some test cases to go
with it). I'd appreciate testing, and suggestions for fixing the
BrokenSymLinkToFile test are welcome.

-- 
dann frazier | HP Open Source and Linux Organization
------------------------------------------------------------------------
r39 | dannf | 2007-04-04 16:43:14 -0600 (Wed, 04 Apr 2007) | 4 lines

Add support for symlinks. Still doesn't support the case where a symlink
gets replaced by a regular file - looks like that will require an extra
commit pass. The BrokenSymLinkToFile test is expected to fail due to this.

------------------------------------------------------------------------
Index: svn-load
===================================================================
--- svn-load	(revision 38)
+++ svn-load	(revision 39)
@@ -191,9 +191,19 @@
                                  % (counterpath, fullpath))
                 return False
             needs_add = False
-            if not os.path.exists(counterpath):
+            if not os.path.lexists(counterpath):
                 needs_add = True
-            shutil.copy(fullpath, counterpath)
+
+            # shutil.copy follows symlinks, so we need to handle them
+            # separately
+            if os.path.lexists(counterpath) and \
+                   (os.path.islink(counterpath) or os.path.islink(fullpath)):
+                os.unlink(counterpath)
+
+            if os.path.islink(fullpath):
+                os.symlink(os.readlink(fullpath), counterpath)
+            else:
+                shutil.copy(fullpath, counterpath)
             if needs_add:
                 client.add(counterpath)
         # We have to use a counter instead of something like 'for d in dirs'
@@ -206,7 +216,7 @@
             counterpath = os.path.join(workingdir, relpath)
 
             if not os.path.exists(counterpath):
-                shutil.copytree(fullpath, counterpath)
+                shutil.copytree(fullpath, counterpath, symlinks=True)
                 client.add(counterpath)
                 dirs.pop(i)
                 continue
@@ -305,7 +315,9 @@
 ## treats u+x as the canonical decider for svn:executable
 ## should probably see if svn import does it differently..
 def is_executable(f):
-    s = os.stat(f)
+    if os.path.islink(f):
+        return False
+    s = os.lstat(f)
     return s[stat.ST_MODE] & 0500 == 0500
 
 def svn_is_executable(file):
Index: test.py
===================================================================
--- test.py	(revision 38)
+++ test.py	(revision 39)
@@ -94,9 +94,27 @@
         else:
             return False
 
+    ## This should allow us to accept cases that dircmp considers "funny".
+    ## The only known case so far is where both trees have the same identical
+    ## symlink
+    def funny_check(self, a, b, funny):
+        haha = True
+        strange = False
+        for f in funny:
+            funnyA = os.path.join(a, f)
+            funnyB = os.path.join(b, f)
+
+            if os.path.islink(funnyA) and os.path.islink(funnyB):
+                if os.readlink(funnyA) == os.readlink(funnyB):
+                    continue
+            else:
+                return strange
+        return haha
+
     def compare_dirs(self, a, b):
         c = filecmp.dircmp(a, b)
-        if c.left_only or c.right_only or c.diff_files or c.common_funny:
+        funny_ok = self.funny_check(a, b, c.common_funny)
+        if c.left_only or c.right_only or c.diff_files or not funny_ok:
             return False
         else:
             return c.common_dirs
@@ -134,6 +152,16 @@
         f.write("baz\n")
         f.close()
 
+class EmptyToSymLink(BaseLoadCase):
+    def loadDirsSetUp(self):
+        BaseLoadCase.loadDirsSetUp(self)
+        os.symlink('baz', os.path.join(self.loaddirs[0], 'foo'))
+
+class EmptyToSingleFileAndSymLink(EmptyToSingleFile):
+    def loadDirsSetUp(self):
+        EmptyToSingleFile.loadDirsSetUp(self)
+        os.symlink('foo', os.path.join(self.loaddirs[0], 'baz'))
+        
 class SingleFileContentChange(BaseLoadCase):
     def importDirSetUp(self):
         BaseLoadCase.importDirSetUp(self)
@@ -160,7 +188,39 @@
         f.write("baz\n")
         f.close()
 
+class SingleFileToBrokenSymLink(BaseLoadCase):
+    def importDirSetUp(self):
+        BaseLoadCase.importDirSetUp(self)
+        f = open(os.path.join(self.importdir, 'foo'), 'w')
+        f.write("bar\n")
+        f.close()
+
+    def loadDirsSetUp(self):
+        BaseLoadCase.loadDirsSetUp(self)
+        os.symlink('broken', os.path.join(self.loaddirs[0], 'foo'))
+
+class SingleFileToBrokenSymLink(BaseLoadCase):
+    def importDirSetUp(self):
+        BaseLoadCase.importDirSetUp(self)
+        f = open(os.path.join(self.importdir, 'foo'), 'w')
+        f.write("bar\n")
+        f.close()
+
+    def loadDirsSetUp(self):
+        BaseLoadCase.loadDirsSetUp(self)
+        os.symlink('broken', os.path.join(self.loaddirs[0], 'foo'))
+
+class BrokenSymLinkToFile(BaseLoadCase):
+    def importDirSetUp(self):
+        BaseLoadCase.importDirSetUp(self)
+        os.symlink('broken', os.path.join(self.importdir, 'foo'))
+
+    def loadDirsSetUp(self):
+        BaseLoadCase.loadDirsSetUp(self)
+        f = open(os.path.join(self.loaddirs[0], 'foo'), 'w')
+        f.write("bar\n")
+        f.close()
+
+
 if __name__ == '__main__':
     unittest.main()
-
-        

Reply via email to