Mercurial > hg
changeset 22296:650b5b6e75ed
convert: use None value for missing files instead of overloading IOError
The internal API used IOError to indicate that a file should be marked as
removed.
There is some correlation between IOError (especially with ENOENT) and files
that should be removed, but using IOErrors to represent file removal internally
required some hacks.
Instead, use the value None to indicate that the file not is present.
Before, spurious IO errors could cause commits that silently removed files.
They will now be reported like all other IO errors so the root cause can be
fixed.
author | Mads Kiilerich <madski@unity3d.com> |
---|---|
date | Tue, 26 Aug 2014 22:03:32 +0200 |
parents | 926bc0d3b595 |
children | a74d05878a8d |
files | hgext/convert/bzr.py hgext/convert/common.py hgext/convert/cvs.py hgext/convert/darcs.py hgext/convert/git.py hgext/convert/gnuarch.py hgext/convert/hg.py hgext/convert/monotone.py hgext/convert/p4.py hgext/convert/subversion.py hgext/histedit.py hgext/largefiles/lfcommands.py mercurial/cmdutil.py mercurial/context.py mercurial/localrepo.py mercurial/patch.py |
diffstat | 16 files changed, 66 insertions(+), 52 deletions(-) [+] |
line wrap: on
line diff
--- a/hgext/convert/bzr.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/bzr.py Tue Aug 26 22:03:32 2014 +0200 @@ -122,8 +122,7 @@ kind = revtree.kind(fileid) if kind not in supportedkinds: # the file is not available anymore - was deleted - raise IOError(_('%s is not available in %s anymore') % - (name, rev)) + return None, None mode = self._modecache[(name, rev)] if kind == 'symlink': target = revtree.get_symlink_target(fileid)
--- a/hgext/convert/common.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/common.py Tue Aug 26 22:03:32 2014 +0200 @@ -88,8 +88,8 @@ def getfile(self, name, rev): """Return a pair (data, mode) where data is the file content as a string and mode one of '', 'x' or 'l'. rev is the - identifier returned by a previous call to getchanges(). Raise - IOError to indicate that name was deleted in rev. + identifier returned by a previous call to getchanges(). + Data is None if file is missing/deleted in rev. """ raise NotImplementedError
--- a/hgext/convert/cvs.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/cvs.py Tue Aug 26 22:03:32 2014 +0200 @@ -220,7 +220,7 @@ self._parse() if rev.endswith("(DEAD)"): - raise IOError + return None, None args = ("-N -P -kk -r %s --" % rev).split() args.append(self.cvsrepo + '/' + name)
--- a/hgext/convert/darcs.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/darcs.py Tue Aug 26 22:03:32 2014 +0200 @@ -8,7 +8,7 @@ from common import NoRepo, checktool, commandline, commit, converter_source from mercurial.i18n import _ from mercurial import util -import os, shutil, tempfile, re +import os, shutil, tempfile, re, errno # The naming drift of ElementTree is fun! @@ -192,8 +192,13 @@ if rev != self.lastrev: raise util.Abort(_('internal calling inconsistency')) path = os.path.join(self.tmppath, name) - data = util.readfile(path) - mode = os.lstat(path).st_mode + try: + data = util.readfile(path) + mode = os.lstat(path).st_mode + except IOError, inst: + if inst.errno == errno.ENOENT: + return None, None + raise mode = (mode & 0111) and 'x' or '' return data, mode
--- a/hgext/convert/git.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/git.py Tue Aug 26 22:03:32 2014 +0200 @@ -135,7 +135,7 @@ def getfile(self, name, rev): if rev == hex(nullid): - raise IOError + return None, None if name == '.hgsub': data = '\n'.join([m.hgsub() for m in self.submoditer()]) mode = ''
--- a/hgext/convert/gnuarch.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/gnuarch.py Tue Aug 26 22:03:32 2014 +0200 @@ -137,9 +137,8 @@ if rev != self.lastrev: raise util.Abort(_('internal calling inconsistency')) - # Raise IOError if necessary (i.e. deleted files). if not os.path.lexists(os.path.join(self.tmppath, name)): - raise IOError + return None, None return self._getfile(name, rev)
--- a/hgext/convert/hg.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/hg.py Tue Aug 26 22:03:32 2014 +0200 @@ -134,6 +134,8 @@ def getfilectx(repo, memctx, f): v = files[f] data, mode = source.getfile(f, v) + if data is None: + return None if f == '.hgtags': data = self._rewritetags(source, revmap, data) return context.memfilectx(self.repo, f, data, 'l' in mode, @@ -351,8 +353,8 @@ try: fctx = self.changectx(rev)[name] return fctx.data(), fctx.flags() - except error.LookupError, err: - raise IOError(err) + except error.LookupError: + return None, None def getchanges(self, rev): ctx = self.changectx(rev)
--- a/hgext/convert/monotone.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/monotone.py Tue Aug 26 22:03:32 2014 +0200 @@ -282,11 +282,11 @@ def getfile(self, name, rev): if not self.mtnisfile(name, rev): - raise IOError # file was deleted or renamed + return None, None try: data = self.mtnrun("get_file_of", name, r=rev) except Exception: - raise IOError # file was deleted or renamed + return None, None self.mtnloadmanifest(rev) node, attr = self.files.get(name, (None, "")) return data, attr
--- a/hgext/convert/p4.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/p4.py Tue Aug 26 22:03:32 2014 +0200 @@ -183,7 +183,7 @@ contents += data if mode is None: - raise IOError(0, "bad stat") + return None, None if keywords: contents = keywords.sub("$\\1$", contents)
--- a/hgext/convert/subversion.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/convert/subversion.py Tue Aug 26 22:03:32 2014 +0200 @@ -933,7 +933,7 @@ def getfile(self, file, rev): # TODO: ra.get_file transmits the whole file instead of diffs. if file in self.removed: - raise IOError + return None, None mode = '' try: new_module, revnum = revsplit(rev)[1:] @@ -954,7 +954,7 @@ notfound = (svn.core.SVN_ERR_FS_NOT_FOUND, svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND) if e.apr_err in notfound: # File not found - raise IOError + return None, None raise if mode == 'l': link_prefix = "link " @@ -1236,9 +1236,8 @@ # Apply changes to working copy for f, v in files: - try: - data, mode = source.getfile(f, v) - except IOError: + data, mode = source.getfile(f, v) + if data is None: self.delete.append(f) else: self.putfile(f, mode, data)
--- a/hgext/histedit.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/histedit.py Tue Aug 26 22:03:32 2014 +0200 @@ -283,7 +283,7 @@ isexec='x' in flags, copied=copied.get(path)) return mctx - raise IOError() + return None if commitopts.get('message'): message = commitopts['message']
--- a/hgext/largefiles/lfcommands.py Wed Aug 27 12:30:28 2014 +0200 +++ b/hgext/largefiles/lfcommands.py Tue Aug 26 22:03:32 2014 +0200 @@ -146,7 +146,7 @@ try: fctx = ctx.filectx(lfutil.standin(f)) except error.LookupError: - raise IOError + return None renamed = fctx.renamed() if renamed: renamed = lfutil.splitstandin(renamed[0]) @@ -248,7 +248,7 @@ try: fctx = ctx.filectx(srcfname) except error.LookupError: - raise IOError + return None renamed = fctx.renamed() if renamed: # standin is always a largefile because largefile-ness @@ -298,7 +298,7 @@ try: fctx = ctx.filectx(f) except error.LookupError: - raise IOError + return None renamed = fctx.renamed() if renamed: renamed = renamed[0]
--- a/mercurial/cmdutil.py Wed Aug 27 12:30:28 2014 +0200 +++ b/mercurial/cmdutil.py Tue Aug 26 22:03:32 2014 +0200 @@ -2130,7 +2130,7 @@ copied=copied.get(path)) return mctx except KeyError: - raise IOError + return None else: ui.note(_('copying changeset %s to %s\n') % (old, base)) @@ -2139,7 +2139,7 @@ try: return old.filectx(path) except KeyError: - raise IOError + return None user = opts.get('user') or old.user() date = opts.get('date') or old.date()
--- a/mercurial/context.py Wed Aug 27 12:30:28 2014 +0200 +++ b/mercurial/context.py Tue Aug 26 22:03:32 2014 +0200 @@ -347,7 +347,10 @@ def makememctx(repo, parents, text, user, date, branch, files, store, editor=None): def getfilectx(repo, memctx, path): - data, (islink, isexec), copied = store.getfile(path) + data, mode, copied = store.getfile(path) + if data is None: + return None + islink, isexec = mode return memfilectx(repo, path, data, islink=islink, isexec=isexec, copied=copied, memctx=memctx) extra = {} @@ -1626,7 +1629,9 @@ self._repo.savecommitmessage(self._text) def filectx(self, path, filelog=None): - """get a file context from the working directory""" + """get a file context from the working directory + + Returns None if file doesn't exist and should be removed.""" return self._filectxfn(self._repo, self, path) def commit(self):
--- a/mercurial/localrepo.py Wed Aug 27 12:30:28 2014 +0200 +++ b/mercurial/localrepo.py Tue Aug 26 22:03:32 2014 +0200 @@ -1394,9 +1394,12 @@ self.ui.note(f + "\n") try: fctx = ctx[f] - new[f] = self._filecommit(fctx, m1, m2, linkrev, trp, - changed) - m1.set(f, fctx.flags()) + if fctx is None: + removed.append(f) + else: + new[f] = self._filecommit(fctx, m1, m2, linkrev, + trp, changed) + m1.set(f, fctx.flags()) except OSError, inst: self.ui.warn(_("trouble committing %s!\n") % f) raise @@ -1404,9 +1407,7 @@ errcode = getattr(inst, 'errno', errno.ENOENT) if error or errcode and errcode != errno.ENOENT: self.ui.warn(_("trouble committing %s!\n") % f) - raise - else: - removed.append(f) + raise # update manifest m1.update(new)
--- a/mercurial/patch.py Wed Aug 27 12:30:28 2014 +0200 +++ b/mercurial/patch.py Tue Aug 26 22:03:32 2014 +0200 @@ -382,7 +382,7 @@ def getfile(self, fname): """Return target file data and flags as a (data, (islink, - isexec)) tuple. + isexec)) tuple. Data is None if file is missing/deleted. """ raise NotImplementedError @@ -426,7 +426,12 @@ except OSError, e: if e.errno != errno.ENOENT: raise - return (self.opener.read(fname), (False, isexec)) + try: + return (self.opener.read(fname), (False, isexec)) + except IOError, e: + if e.errno != errno.ENOENT: + raise + return None, None def setfile(self, fname, data, mode, copysource): islink, isexec = mode @@ -528,7 +533,7 @@ if fname in self.data: return self.data[fname] if not self.opener or fname not in self.files: - raise IOError + return None, None, None fn, mode, copied = self.files[fname] return self.opener.read(fn), mode, copied @@ -554,7 +559,7 @@ try: fctx = self.ctx[fname] except error.LookupError: - raise IOError + return None, None flags = fctx.flags() return fctx.data(), ('l' in flags, 'x' in flags) @@ -597,13 +602,12 @@ self.copysource = gp.oldpath self.create = gp.op in ('ADD', 'COPY', 'RENAME') self.remove = gp.op == 'DELETE' - try: - if self.copysource is None: - data, mode = backend.getfile(self.fname) - self.exists = True - else: - data, mode = store.getfile(self.copysource)[:2] - self.exists = backend.exists(self.fname) + if self.copysource is None: + data, mode = backend.getfile(self.fname) + else: + data, mode = store.getfile(self.copysource)[:2] + if data is not None: + self.exists = self.copysource is None or backend.exists(self.fname) self.missing = False if data: self.lines = mdiff.splitnewlines(data) @@ -622,7 +626,7 @@ l = l[:-2] + '\n' nlines.append(l) self.lines = nlines - except IOError: + else: if self.create: self.missing = False if self.mode is None: @@ -1380,6 +1384,8 @@ data, mode = None, None if gp.op in ('RENAME', 'COPY'): data, mode = store.getfile(gp.oldpath)[:2] + # FIXME: failing getfile has never been handled here + assert data is not None if gp.mode: mode = gp.mode if gp.op == 'ADD': @@ -1404,15 +1410,13 @@ elif state == 'git': for gp in values: path = pstrip(gp.oldpath) - try: - data, mode = backend.getfile(path) - except IOError, e: - if e.errno != errno.ENOENT: - raise + data, mode = backend.getfile(path) + if data is None: # The error ignored here will trigger a getfile() # error in a place more appropriate for error # handling, and will not interrupt the patching # process. + pass else: store.setfile(path, data, mode) else: