Commits

Markus Zapke-Gründemann  committed c043d55

Symlinks containing backslashes are dereferenced.

If the path of a symlink on Linux contains backward slashes (i.e. the link was
intended for Windows), the slashes are converted to forward slashes and the
link is dereferenced.

  • Participants
  • Parent commits d3591e9

Comments (0)

Files changed (3)

 1.1.0
 =====
 
-- Symlinks are no longer dereferenced on Linux.
+- Symlinks are no longer dereferenced on Linux (but still dereferenced on
+  Windows).
+- If the path of a symlink on Linux contains backward slashes (i.e. the link
+  was intended for Windows), the slashes are converted to forward slashes and
+  the link is dereferenced.
 - Renamed --exclude option to --exclude-pattern.
 - Added new option --exclude-path to exclude a path relative to SOURCE.
 - Added new option --retain-empty-dirs to import empty directories.
                 onerror(os.remove, path)
 
 
+def contains_backslashes(path):
+    """Resolves a symlink and checks if the path contains backslashes."""
+    return '\\' in os.readlink(path)
+
+
+def replace_backslashes(path):
+    """Replaces all backslashes in a symlink target with slashes.
+
+    Returns the symlink's target.
+    """
+    return os.readlink(path).replace('\\', '/')
+
+
+def dereference_symlink(path, resolve=True):
+    """Returns a dereferenced symlink.
+
+    If the symlink contains backslashes they are replaced with slashes.
+    """
+    linkto = replace_backslashes(path)
+    return os.path.join(os.path.dirname(path), linkto)
+
+
 def smartcopy(src, dst, node, ignore=None, exclude_path=None, no_errors=False):
     """Copies a node (recursively) from src to dst directory.
 
             # Terminate immediately if the node matches the exclude pattern.
             return warnings
     # Either create a symlink or copy the files recursively.
+    is_symlink = os.path.islink(srcpath)
     symlinks = True
     if os.name == 'nt':
         # Dereference symlinks for Windows.
         symlinks = False
-    if symlinks and os.path.islink(srcpath):
+    if symlinks and is_symlink:
+        if contains_backslashes(srcpath):
+            symlinks = False
+            srcpath = dereference_symlink(srcpath)
+    if symlinks and is_symlink:
         linkto = os.readlink(srcpath)
         os.symlink(linkto, dstpath)
     else:
             if common_suffix in exclude_path:
                 continue
         try:
-            if symlinks and os.path.islink(srcname):
+            is_symlink = os.path.islink(srcname)
+            if symlinks and is_symlink:
+                if contains_backslashes(srcname):
+                    symlinks = False
+                    srcname = dereference_symlink(srcname)
+            if symlinks and is_symlink:
                 linkto = os.readlink(srcname)
                 os.symlink(linkto, dstname)
             elif os.path.isdir(srcname):

File test-importfs-symlinks.t

   $ ls -la r2/d4/
   ls: r2/d4/: No such file or directory
   [2]
+
+Clean up:
+
+  $ rm -f d1/d4
+
+Create symlinks with backslashes:
+
+  $ mkdir d1/d2
+  $ echo c3 > d1/d2/f3
+  $ cd d1/d2
+  $ ln -s "..\f1" f4
+  $ cd ../..
+  $ cat d1/d2/f4
+  cat: d1/d2/f4: No such file or directory
+  [1]
+  $ ln -s "d2\f3" d1/f6
+  $ cat d1/f6
+  cat: d1/f6: No such file or directory
+  [1]
+
+Now run the import:
+
+  $ hg importfs r3 d1
+  created repository $TESTTMP/r3
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  adding d2/f3
+  adding d2/f4
+  adding d3/f5
+  adding f1
+  adding f2
+  adding f6
+
+The backslashes are replaced and the symlinks containing them before are dereferenced:
+
+  $ hg manifest -v -R r3
+  644   d2/f3
+  644   d2/f4
+  644 @ d3/f5
+  644   f1
+  644   f2
+  644   f6
+  $ cat r3/d2/f4
+  c1
+  $ cat r3/f6
+  c3
+