diff --git a/contrib/undumprevlog b/contrib/undumprevlog --- a/contrib/undumprevlog +++ b/contrib/undumprevlog @@ -4,12 +4,12 @@ # $ undumprevlog < repo.dump import sys -from mercurial import revlog, node, util, transaction +from mercurial import revlog, node, scmutil, util, transaction for fp in (sys.stdin, sys.stdout, sys.stderr): util.set_binary(fp) -opener = util.opener('.', False) +opener = scmutil.opener('.', False) tr = transaction.transaction(sys.stderr.write, opener, "undump.journal") while 1: l = sys.stdin.readline() diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py --- a/hgext/convert/subversion.py +++ b/hgext/convert/subversion.py @@ -10,7 +10,7 @@ import tempfile import urllib import urllib2 -from mercurial import strutil, util, encoding +from mercurial import strutil, scmutil, util, encoding from mercurial.i18n import _ # Subversion stuff. Works best with very recent Python SVN bindings @@ -998,8 +998,8 @@ class svn_sink(converter_sink, commandli self.run0('checkout', path, wcpath) self.wc = wcpath - self.opener = util.opener(self.wc) - self.wopener = util.opener(self.wc) + self.opener = scmutil.opener(self.wc) + self.wopener = scmutil.opener(self.wc) self.childmap = mapfile(ui, self.join('hg-childmap')) self.is_exec = util.checkexec(self.wc) and util.is_exec or None diff --git a/hgext/extdiff.py b/hgext/extdiff.py --- a/hgext/extdiff.py +++ b/hgext/extdiff.py @@ -61,7 +61,7 @@ pretty fast (at least faster than having from mercurial.i18n import _ from mercurial.node import short, nullid -from mercurial import cmdutil, util, commands, encoding +from mercurial import cmdutil, scmutil, util, commands, encoding import os, shlex, shutil, tempfile, re def snapshot(ui, repo, files, node, tmproot): @@ -81,7 +81,7 @@ def snapshot(ui, repo, files, node, tmpr else: ui.note(_('making snapshot of %d files from working directory\n') % (len(files))) - wopener = util.opener(base) + wopener = scmutil.opener(base) fns_and_mtime = [] ctx = repo[node] for fn in files: diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -45,7 +45,7 @@ create other, independent patch queues w from mercurial.i18n import _ from mercurial.node import bin, hex, short, nullid, nullrev from mercurial.lock import release -from mercurial import commands, cmdutil, hg, patch, util +from mercurial import commands, cmdutil, hg, patch, scmutil, util from mercurial import repair, extensions, url, error import os, sys, re, errno, shutil @@ -259,7 +259,7 @@ class queue(object): except IOError: curpath = os.path.join(path, 'patches') self.path = patchdir or curpath - self.opener = util.opener(self.path) + self.opener = scmutil.opener(self.path) self.ui = ui self.applied_dirty = 0 self.series_dirty = 0 diff --git a/hgext/transplant.py b/hgext/transplant.py --- a/hgext/transplant.py +++ b/hgext/transplant.py @@ -16,7 +16,7 @@ map from a changeset hash to its hash in from mercurial.i18n import _ import os, tempfile from mercurial import bundlerepo, cmdutil, hg, merge, match -from mercurial import patch, revlog, util, error +from mercurial import patch, revlog, scmutil, util, error from mercurial import revset, templatekw class transplantentry(object): @@ -31,7 +31,7 @@ class transplants(object): self.opener = opener if not opener: - self.opener = util.opener(self.path) + self.opener = scmutil.opener(self.path) self.transplants = {} self.dirty = False self.read() @@ -74,7 +74,7 @@ class transplanter(object): def __init__(self, ui, repo): self.ui = ui self.path = repo.join('transplant') - self.opener = util.opener(self.path) + self.opener = scmutil.opener(self.path) self.transplants = transplants(self.path, 'transplants', opener=self.opener) diff --git a/mercurial/archival.py b/mercurial/archival.py --- a/mercurial/archival.py +++ b/mercurial/archival.py @@ -8,7 +8,7 @@ from i18n import _ from node import hex import cmdutil -import util, encoding +import scmutil, util, encoding import cStringIO, os, tarfile, time, zipfile import zlib, gzip @@ -187,7 +187,7 @@ class fileit(object): def __init__(self, name, mtime): self.basedir = name - self.opener = util.opener(self.basedir) + self.opener = scmutil.opener(self.basedir) def addfile(self, name, mode, islink, data): if islink: diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -9,7 +9,7 @@ from node import hex, bin, nullid, nullr from lock import release from i18n import _, gettext import os, re, sys, difflib, time, tempfile -import hg, util, revlog, extensions, copies, error, bookmarks +import hg, scmutil, util, revlog, extensions, copies, error, bookmarks import patch, help, mdiff, url, encoding, templatekw, discovery import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server import merge as mergemod @@ -975,7 +975,7 @@ def debugancestor(ui, repo, *args): """find the ancestor revision of two revisions in a given index""" if len(args) == 3: index, rev1, rev2 = args - r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) + r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index) lookup = r.lookup elif len(args) == 2: if not repo: @@ -1393,7 +1393,7 @@ def debugdag(ui, repo, file_=None, *revs spaces = opts.get('spaces') dots = opts.get('dots') if file_: - rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_) revs = set((int(r) for r in revs)) def events(): for r in rlog: @@ -1443,7 +1443,8 @@ def debugdata(ui, repo, file_, rev): if len(filelog): r = filelog if not r: - r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i") + r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), + file_[:-2] + ".i") try: ui.write(r.revision(r.lookup(rev))) except KeyError: @@ -1482,7 +1483,7 @@ def debugindex(ui, repo, file_, **opts): raise util.Abort(_("unknown format %d") % format) if not r: - r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_) if format == 0: ui.write(" rev offset length base linkrev" @@ -1515,7 +1516,7 @@ def debugindexdot(ui, repo, file_): if len(filelog): r = filelog if not r: - r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_) ui.write("digraph G {\n") for i in r: node = r.node(i) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -10,7 +10,7 @@ from i18n import _ import repo, changegroup, subrepo, discovery, pushkey import changelog, dirstate, filelog, manifest, context, bookmarks import lock, transaction, store, encoding -import util, extensions, hook, error +import scmutil, util, extensions, hook, error import match as matchmod import merge as mergemod import tags as tagsmod @@ -32,8 +32,8 @@ class localrepository(repo.repository): self.path = os.path.join(self.root, ".hg") self.origroot = path self.auditor = util.path_auditor(self.root, self._checknested) - self.opener = util.opener(self.path) - self.wopener = util.opener(self.root) + self.opener = scmutil.opener(self.path) + self.wopener = scmutil.opener(self.root) self.baseui = baseui self.ui = baseui.copy() @@ -90,7 +90,7 @@ class localrepository(repo.repository): if inst.errno != errno.ENOENT: raise - self.store = store.store(requirements, self.sharedpath, util.opener) + self.store = store.store(requirements, self.sharedpath, scmutil.opener) self.spath = self.store.path self.sopener = self.store.opener self.sjoin = self.store.join diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -11,7 +11,7 @@ import tempfile, zlib from i18n import _ from node import hex, nullid, short -import base85, mdiff, util, diffhelpers, copies, encoding +import base85, mdiff, scmutil, util, diffhelpers, copies, encoding gitre = re.compile('diff --git a/(.*) b/(.*)') @@ -1111,7 +1111,7 @@ def _applydiff(ui, fp, patcher, copyfn, err = 0 current_file = None cwd = os.getcwd() - opener = util.opener(cwd) + opener = scmutil.opener(cwd) for state, values in iterhunks(ui, fp): if state == 'hunk': diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -7,7 +7,7 @@ from i18n import _ import util, error -import os +import os, errno def checkportable(ui, f): '''Check if filename f is portable and warn or abort depending on config''' @@ -25,3 +25,98 @@ def checkportable(ui, f): elif bval is None and lval != 'ignore': raise error.ConfigError( _("ui.portablefilenames value is invalid ('%s')") % val) + +class opener(object): + '''Open files relative to a base directory + + This class is used to hide the details of COW semantics and + remote file access from higher level code. + ''' + def __init__(self, base, audit=True): + self.base = base + if audit: + self.auditor = util.path_auditor(base) + else: + self.auditor = util.always + self.createmode = None + self._trustnlink = None + + @util.propertycache + def _can_symlink(self): + return util.checklink(self.base) + + def _fixfilemode(self, name): + if self.createmode is None: + return + os.chmod(name, self.createmode & 0666) + + def __call__(self, path, mode="r", text=False, atomictemp=False): + r = util.checkosfilename(path) + if r: + raise Abort("%s: %r" % (r, path)) + self.auditor(path) + f = os.path.join(self.base, path) + + if not text and "b" not in mode: + mode += "b" # for that other OS + + nlink = -1 + dirname, basename = os.path.split(f) + # If basename is empty, then the path is malformed because it points + # to a directory. Let the posixfile() call below raise IOError. + if basename and mode not in ('r', 'rb'): + if atomictemp: + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + return util.atomictempfile(f, mode, self.createmode) + try: + if 'w' in mode: + util.unlink(f) + nlink = 0 + else: + # nlinks() may behave differently for files on Windows + # shares if the file is open. + fd = util.posixfile(f) + nlink = util.nlinks(f) + if nlink < 1: + nlink = 2 # force mktempcopy (issue1922) + fd.close() + except (OSError, IOError), e: + if e.errno != errno.ENOENT: + raise + nlink = 0 + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + if nlink > 0: + if self._trustnlink is None: + self._trustnlink = nlink > 1 or util.checknlink(f) + if nlink > 1 or not self._trustnlink: + util.rename(util.mktempcopy(f), f) + fp = util.posixfile(f, mode) + if nlink == 0: + self._fixfilemode(f) + return fp + + def symlink(self, src, dst): + self.auditor(dst) + linkname = os.path.join(self.base, dst) + try: + os.unlink(linkname) + except OSError: + pass + + dirname = os.path.dirname(linkname) + if not os.path.exists(dirname): + util.makedirs(dirname, self.createmode) + + if self._can_symlink: + try: + os.symlink(src, linkname) + except OSError, err: + raise OSError(err.errno, _('could not symlink to %r: %s') % + (src, err.strerror), linkname) + else: + f = self(dst, "w") + f.write(src) + f.close() + self._fixfilemode(dst) diff --git a/mercurial/simplemerge.py b/mercurial/simplemerge.py --- a/mercurial/simplemerge.py +++ b/mercurial/simplemerge.py @@ -18,7 +18,7 @@ # s: "i hate that." from i18n import _ -import util, mdiff +import scmutil, util, mdiff import sys, os class CantReprocessAndShowBase(Exception): @@ -429,7 +429,7 @@ def simplemerge(ui, local, base, other, local = os.path.realpath(local) if not opts.get('print'): - opener = util.opener(os.path.dirname(local)) + opener = scmutil.opener(os.path.dirname(local)) out = opener(os.path.basename(local), "w", atomictemp=True) else: out = sys.stdout diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -895,101 +895,6 @@ def makedirs(name, mode=None): makedirs(parent, mode) makedirs(name, mode) -class opener(object): - """Open files relative to a base directory - - This class is used to hide the details of COW semantics and - remote file access from higher level code. - """ - def __init__(self, base, audit=True): - self.base = base - if audit: - self.auditor = path_auditor(base) - else: - self.auditor = always - self.createmode = None - self._trustnlink = None - - @propertycache - def _can_symlink(self): - return checklink(self.base) - - def _fixfilemode(self, name): - if self.createmode is None: - return - os.chmod(name, self.createmode & 0666) - - def __call__(self, path, mode="r", text=False, atomictemp=False): - r = checkosfilename(path) - if r: - raise Abort("%s: %r" % (r, path)) - self.auditor(path) - f = os.path.join(self.base, path) - - if not text and "b" not in mode: - mode += "b" # for that other OS - - nlink = -1 - dirname, basename = os.path.split(f) - # If basename is empty, then the path is malformed because it points - # to a directory. Let the posixfile() call below raise IOError. - if basename and mode not in ('r', 'rb'): - if atomictemp: - if not os.path.isdir(dirname): - makedirs(dirname, self.createmode) - return atomictempfile(f, mode, self.createmode) - try: - if 'w' in mode: - unlink(f) - nlink = 0 - else: - # nlinks() may behave differently for files on Windows - # shares if the file is open. - fd = posixfile(f) - nlink = nlinks(f) - if nlink < 1: - nlink = 2 # force mktempcopy (issue1922) - fd.close() - except (OSError, IOError), e: - if e.errno != errno.ENOENT: - raise - nlink = 0 - if not os.path.isdir(dirname): - makedirs(dirname, self.createmode) - if nlink > 0: - if self._trustnlink is None: - self._trustnlink = nlink > 1 or checknlink(f) - if nlink > 1 or not self._trustnlink: - rename(mktempcopy(f), f) - fp = posixfile(f, mode) - if nlink == 0: - self._fixfilemode(f) - return fp - - def symlink(self, src, dst): - self.auditor(dst) - linkname = os.path.join(self.base, dst) - try: - os.unlink(linkname) - except OSError: - pass - - dirname = os.path.dirname(linkname) - if not os.path.exists(dirname): - makedirs(dirname, self.createmode) - - if self._can_symlink: - try: - os.symlink(src, linkname) - except OSError, err: - raise OSError(err.errno, _('could not symlink to %r: %s') % - (src, err.strerror), linkname) - else: - f = self(dst, "w") - f.write(src) - f.close() - self._fixfilemode(dst) - class chunkbuffer(object): """Allow arbitrary sized chunks of data to be efficiently read from an iterator over chunks of arbitrary size.""" diff --git a/tests/test-parseindex.t b/tests/test-parseindex.t --- a/tests/test-parseindex.t +++ b/tests/test-parseindex.t @@ -26,7 +26,7 @@ We approximate that by reducing the read summary: change foo $ cat >> test.py << EOF - > from mercurial import changelog, util + > from mercurial import changelog, scmutil > from mercurial.node import * > > class singlebyteread(object): @@ -42,7 +42,7 @@ We approximate that by reducing the read > return getattr(self.real, key) > > def opener(*args): - > o = util.opener(*args) + > o = scmutil.opener(*args) > def wrapper(*a): > f = o(*a) > return singlebyteread(f)