diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py --- a/hgext/convert/subversion.py +++ b/hgext/convert/subversion.py @@ -997,7 +997,7 @@ class svn_sink(converter_sink, commandli fp = open(hook, 'w') fp.write(pre_revprop_change) fp.close() - util.set_flags(hook, "x") + util.set_flags(hook, False, True) xport = transport.SvnRaTransport(url=geturl(path)) self.uuid = svn.ra.get_uuid(xport.ra) @@ -1024,7 +1024,7 @@ class svn_sink(converter_sink, commandli # systematically is just as expensive and much simpler. was_exec = 'x' not in flags - util.set_flags(self.wjoin(filename), flags) + util.set_flags(self.wjoin(filename), False, 'x' in flags) if was_exec: if 'x' not in flags: self.delexec.append(filename) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -544,8 +544,12 @@ class localrepository(repo.repository): os.unlink(self.wjoin(filename)) except OSError: pass - self.wopener(filename, 'w').write(data) - util.set_flags(self.wjoin(filename), flags) + if 'l' in flags: + self.wopener.symlink(data, filename) + else: + self.wopener(filename, 'w').write(data) + if 'x' in flags: + util.set_flags(self.wjoin(filename), False, True) def wwritedata(self, filename, data): return self._filter("decode", filename, data) diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -323,6 +323,10 @@ def applyupdates(repo, action, wctx, mct updated += 1 else: merged += 1 + util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) + if f != fd and move and util.lexists(repo.wjoin(f)): + repo.ui.debug(_("removing %s\n") % f) + os.unlink(repo.wjoin(f)) elif m == "g": # get flags = a[2] repo.ui.note(_("getting %s\n") % f) @@ -348,7 +352,7 @@ def applyupdates(repo, action, wctx, mct repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] - util.set_flags(repo.wjoin(f), flags) + util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags) return updated, merged, removed, unresolved diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -1108,7 +1108,7 @@ def updatedir(ui, repo, patches): if ctype == 'ADD' and not os.path.exists(dst): repo.wwrite(gp.path, '', flags) else: - util.set_flags(dst, flags) + util.set_flags(dst, 'l' in flags, 'x' in flags) cmdutil.addremove(repo, cfiles) files = patches.keys() files.extend([r for r in removes if r not in files]) diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -1069,7 +1069,7 @@ if os.name == 'nt': '''return False if pid dead, True if running or not known''' return True - def set_flags(f, flags): + def set_flags(f, l, x): pass def set_binary(fd): @@ -1216,16 +1216,18 @@ else: """check whether a file is executable""" return (os.lstat(f).st_mode & 0100 != 0) - def set_flags(f, flags): + def set_flags(f, l, x): s = os.lstat(f).st_mode - x = "x" in flags - l = "l" in flags if l: if not stat.S_ISLNK(s): # switch file to link data = file(f).read() os.unlink(f) - os.symlink(data, f) + try: + os.symlink(data, f) + except: + # failed to make a link, rewrite file + file(f, "w").write(data) # no chmod needed at this point return if stat.S_ISLNK(s): diff --git a/mercurial/util_win32.py b/mercurial/util_win32.py --- a/mercurial/util_win32.py +++ b/mercurial/util_win32.py @@ -16,6 +16,7 @@ import win32api import errno, os, sys, pywintypes, win32con, win32file, win32process import cStringIO, winerror import osutil +import util from win32com.shell import shell,shellcon class WinError: @@ -201,21 +202,17 @@ def lookup_reg(key, valname=None, scope= except ImportError: return None - def query_val(scope, key, valname): - try: - keyhandle = OpenKey(scope, key) - return QueryValueEx(keyhandle, valname)[0] - except EnvironmentError: - return None - if scope is None: scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE) elif not isinstance(scope, (list, tuple)): scope = (scope,) for s in scope: - val = query_val(s, key, valname) - if val is not None: - return val + try: + val = QueryValueEx(OpenKey(s, key), valname)[0] + # never let a Unicode string escape into the wild + return util.tolocal(val.encode('UTF-8')) + except EnvironmentError: + pass def system_rcpath_win32(): '''return default os-specific hgrc search path''' diff --git a/tests/test-symlink-os-yes-fs-no.py b/tests/test-symlink-os-yes-fs-no.py new file mode 100644 --- /dev/null +++ b/tests/test-symlink-os-yes-fs-no.py @@ -0,0 +1,17 @@ +import os, sys +from mercurial import hg, ui + +TESTDIR = os.environ["TESTDIR"] + +# only makes sense to test on os which supports symlinks +if not hasattr(os, "symlink"): + sys.exit(80) # SKIPPED_STATUS defined in run-tests.py + +# this is what symlink would do on a non-symlink file system +def symlink_failure(src, dst): + raise OSError, (1, "Operation not permitted") +os.symlink = symlink_failure + +# now try cloning a repo which contains symlinks +u = ui.ui() +hg.clone(u, os.path.join(TESTDIR, 'test-no-symlinks.hg'), 'test1') diff --git a/tests/test-symlink-os-yes-fs-no.py.out b/tests/test-symlink-os-yes-fs-no.py.out new file mode 100644 --- /dev/null +++ b/tests/test-symlink-os-yes-fs-no.py.out @@ -0,0 +1,7 @@ +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 4 changes to 4 files +updating working directory +4 files updated, 0 files merged, 0 files removed, 0 files unresolved