# HG changeset patch # User Patrick Mezard # Date 2008-07-20 18:00:02 # Node ID fdf5980bd010c02e5284bacbebd751a4d948061e # Parent 2134d6c09432e4e3dbee18d93ec9242a332f7cdc # Parent 70ecce68df7c9adad2e768abd5c54a493fc5d591 Merge with main test-remove is still failing for status() does not return removed files in a sorted list. We can live with this for now, a fix is coming soon. diff --git a/hgext/hgk.py b/hgext/hgk.py --- a/hgext/hgk.py +++ b/hgext/hgk.py @@ -104,7 +104,8 @@ def catcommit(ui, repo, n, prefix, ctx=N ctx = repo[n] ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ?? for p in ctx.parents(): - ui.write("parent %s\n" % short(p.node())) + ui.write("parent %s\n" % p) + date = ctx.date() description = ctx.description().replace("\0", "") lines = description.splitlines() diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -472,13 +472,9 @@ class workingctx(changectx): self._date = util.parsedate(date) else: self._date = util.makedate() - if user: - self._user = user - else: - self._user = self._repo.ui.username() + self._user = user if parents: - p1, p2 = parents - self._parents = [changectx(self._repo, p) for p in (p1, p2)] + self._parents = [changectx(self._repo, p) for p in parents] if changes: self._status = list(changes) @@ -501,6 +497,9 @@ class workingctx(changectx): def __nonzero__(self): return True + def __contains__(self, key): + return self._dirstate[f] not in "?r" + def __getattr__(self, name): if name == '_status': self._status = self._repo.status(unknown=True) @@ -541,7 +540,7 @@ class workingctx(changectx): def manifest(self): return self._manifest - def user(self): return self._user + def user(self): return self._user or self._repo.ui.username() def date(self): return self._date def description(self): return self._text def files(self): @@ -701,7 +700,7 @@ class memctx(object): self._node = None self._text = text self._date = date and util.parsedate(date) or util.makedate() - self._user = user or self._repo.ui.username() + self._user = user parents = [(p or nullid) for p in parents] p1, p2 = parents self._parents = [changectx(self._repo, p) for p in (p1, p2)] @@ -724,7 +723,7 @@ class memctx(object): def __nonzero__(self): return True - def user(self): return self._user + def user(self): return self._user or self._repo.ui.username() def date(self): return self._date def description(self): return self._text def files(self): return self.modified() diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -9,12 +9,20 @@ of the GNU General Public License, incor from node import nullid from i18n import _ -import struct, os, bisect, stat, strutil, util, errno, ignore +import struct, os, bisect, stat, util, errno, ignore import cStringIO, osutil, sys _unknown = ('?', 0, 0, 0) _format = ">cllll" +def _finddirs(path): + pos = len(path) + while 1: + pos = path.rfind('/', 0, pos) + if pos == -1: + break + yield path[:pos] + class dirstate(object): def __init__(self, opener, ui, root): @@ -55,10 +63,12 @@ class dirstate(object): if err.errno != errno.ENOENT: raise return self._pl elif name == '_dirs': - self._dirs = {} - for f in self._map: - if self[f] != 'r': - self._incpath(f) + dirs = {} + for f,s in self._map.items(): + if s[0] != 'r': + for base in _finddirs(f): + dirs[base] = dirs.get(base, 0) + 1 + self._dirs = dirs return self._dirs elif name == '_ignore': files = [self._join('.hgignore')] @@ -223,67 +233,39 @@ class dirstate(object): def copies(self): return self._copymap - def _incpath(self, path): - c = path.rfind('/') - if c >= 0: + def _droppath(self, f): + if self[f] not in "?r" and "_dirs" in self.__dict__: dirs = self._dirs - base = path[:c] - if base not in dirs: - self._incpath(base) - dirs[base] = 1 - else: - dirs[base] += 1 - - def _decpath(self, path): - c = path.rfind('/') - if c >= 0: - base = path[:c] - dirs = self._dirs - if dirs[base] == 1: - del dirs[base] - self._decpath(base) - else: - dirs[base] -= 1 + for base in _finddirs(f): + if dirs[base] == 1: + del dirs[base] + else: + dirs[base] -= 1 - def _incpathcheck(self, f): - if '\r' in f or '\n' in f: - raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") - % f) - # shadows - if f in self._dirs: - raise util.Abort(_('directory %r already in dirstate') % f) - for c in strutil.rfindall(f, '/'): - d = f[:c] - if d in self._dirs: - break - if d in self._map and self[d] != 'r': - raise util.Abort(_('file %r in dirstate clashes with %r') % - (d, f)) - self._incpath(f) - - def _changepath(self, f, newstate, relaxed=False): - # handle upcoming path changes + def _addpath(self, f, check=False): oldstate = self[f] - if oldstate not in "?r" and newstate in "?r": - if "_dirs" in self.__dict__: - self._decpath(f) - return - if oldstate in "?r" and newstate not in "?r": - if relaxed and oldstate == '?': - # XXX - # in relaxed mode we assume the caller knows - # what it is doing, workaround for updating - # dir-to-file revisions - if "_dirs" in self.__dict__: - self._incpath(f) - return - self._incpathcheck(f) - return + if check or oldstate == "r": + if '\r' in f or '\n' in f: + raise util.Abort( + _("'\\n' and '\\r' disallowed in filenames: %r") % f) + if f in self._dirs: + raise util.Abort(_('directory %r already in dirstate') % f) + # shadows + for d in _finddirs(f): + if d in self._dirs: + break + if d in self._map and self[d] != 'r': + raise util.Abort( + _('file %r in dirstate clashes with %r') % (d, f)) + if oldstate in "?r" and "_dirs" in self.__dict__: + dirs = self._dirs + for base in _finddirs(f): + dirs[base] = dirs.get(base, 0) + 1 def normal(self, f): 'mark a file normal and clean' self._dirty = True - self._changepath(f, 'n', True) + self._addpath(f) s = os.lstat(self._join(f)) self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0) if f in self._copymap: @@ -307,7 +289,7 @@ class dirstate(object): if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: return self._dirty = True - self._changepath(f, 'n', True) + self._addpath(f) self._map[f] = ('n', 0, -1, -1, 0) if f in self._copymap: del self._copymap[f] @@ -315,7 +297,7 @@ class dirstate(object): def normaldirty(self, f): 'mark a file normal, but dirty' self._dirty = True - self._changepath(f, 'n', True) + self._addpath(f) self._map[f] = ('n', 0, -2, -1, 0) if f in self._copymap: del self._copymap[f] @@ -323,7 +305,7 @@ class dirstate(object): def add(self, f): 'mark a file added' self._dirty = True - self._changepath(f, 'a') + self._addpath(f, True) self._map[f] = ('a', 0, -1, -1, 0) if f in self._copymap: del self._copymap[f] @@ -331,7 +313,7 @@ class dirstate(object): def remove(self, f): 'mark a file removed' self._dirty = True - self._changepath(f, 'r') + self._droppath(f) size = 0 if self._pl[1] != nullid and f in self._map: entry = self._map[f] @@ -347,7 +329,7 @@ class dirstate(object): 'mark a file merged' self._dirty = True s = os.lstat(self._join(f)) - self._changepath(f, 'm', True) + self._addpath(f) self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0) if f in self._copymap: del self._copymap[f] @@ -356,7 +338,7 @@ class dirstate(object): 'forget a file' self._dirty = True try: - self._changepath(f, '?') + self._droppath(f) del self._map[f] except KeyError: self._ui.warn(_("not in dirstate: %s\n") % f) @@ -467,8 +449,8 @@ class dirstate(object): return False if self._ignore(f): return True - for c in strutil.findall(f, '/'): - if self._ignore(f[:c]): + for p in _finddirs(f): + if self._ignore(p): return True return False @@ -606,7 +588,7 @@ class dirstate(object): known[nn] = 1 if match(nf): if supported(ff, st.st_mode, verbose=True): - yield 'f', self.normalize(nf), st + yield 'f', nn, st elif ff in dc: yield 'm', nf, st diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -21,7 +21,7 @@ helptable = ( "13:18" (today assumed) "3:39" (3:39AM assumed) "3:39pm" (15:39) - "2006-12-6 13:18:29" (ISO 8601 format) + "2006-12-06 13:18:29" (ISO 8601 format) "2006-12-6 13:18" "2006-12-6" "12-6" diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -943,7 +943,7 @@ class localrepository(repo.repository): ''' return self[node].walk(match) - def status(self, node1=None, node2=None, match=None, + def status(self, node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False): """return status of files between two nodes or node and working directory @@ -951,111 +951,84 @@ class localrepository(repo.repository): If node2 is None, compare node1 with working directory. """ - def fcmp(fn, getnode): - t1 = self.wread(fn) - return self.file(fn).cmp(getnode(fn), t1) - - def mfmatches(node): - change = self.changelog.read(node) - mf = self.manifest.read(change[0]).copy() + def mfmatches(ctx): + mf = ctx.manifest().copy() for fn in mf.keys(): if not match(fn): del mf[fn] return mf - if not match: - match = match_.always(self.root, self.getcwd()) - + ctx1 = self[node1] + ctx2 = self[node2] + working = ctx2 == self[None] + parentworking = working and ctx1 == self['.'] + match = match or match_.always(self.root, self.getcwd()) listignored, listclean, listunknown = ignored, clean, unknown - modified, added, removed, deleted, unknown = [], [], [], [], [] - ignored, clean = [], [] - compareworking = False - if not node1 or (not node2 and node1 == self.dirstate.parents()[0]): - compareworking = True - - if not compareworking: - # read the manifest from node1 before the manifest from node2, - # so that we'll hit the manifest cache if we're going through - # all the revisions in parent->child order. - mf1 = mfmatches(node1) + if working: # we need to scan the working dir + s = self.dirstate.status(match, listignored, listclean, listunknown) + cmp, modified, added, removed, deleted, unknown, ignored, clean = s - # are we comparing the working directory? - if not node2: - (lookup, modified, added, removed, deleted, unknown, - ignored, clean) = self.dirstate.status(match, listignored, - listclean, listunknown) - # are we comparing working dir against its parent? - if compareworking: - if lookup: - fixup = [] - # do a full compare of any files that might have changed - ctx = self['.'] - ff = self.dirstate.flagfunc(ctx.flags) - for f in lookup: - if (f not in ctx or ff(f) != ctx.flags(f) - or ctx[f].cmp(self.wread(f))): - modified.append(f) - else: - fixup.append(f) - if listclean: - clean.append(f) + # check for any possibly clean files + if parentworking and cmp: + fixup = [] + # do a full compare of any files that might have changed + for f in cmp: + if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f) + or ctx1[f].cmp(ctx2[f].data())): + modified.append(f) + else: + fixup.append(f) + + modified.sort() + if listclean: + clean = util.sort(clean + fixup) - # update dirstate for files that are actually clean - if fixup: - wlock = None + # update dirstate for files that are actually clean + if fixup: + wlock = None + try: try: - try: - wlock = self.wlock(False) - except lock.LockException: - pass - if wlock: - for f in fixup: - self.dirstate.normal(f) - finally: - del wlock - else: + wlock = self.wlock(False) + for f in fixup: + self.dirstate.normal(f) + except lock.LockException: + pass + finally: + del wlock + + if not parentworking: + mf1 = mfmatches(ctx1) + if working: # we are comparing working dir against non-parent # generate a pseudo-manifest for the working dir - # XXX: create it in dirstate.py ? - mf2 = mfmatches(self.dirstate.parents()[0]) - ff = self.dirstate.flagfunc(mf2.flags) - for f in lookup + modified + added: - mf2[f] = "" - mf2.set(f, ff(f)) + mf2 = mfmatches(self['.']) + mf2.flags = ctx2.flags # delay flag lookup + for f in cmp + modified + added: + mf2[f] = None for f in removed: if f in mf2: del mf2[f] - - else: - # we are comparing two revisions - mf2 = mfmatches(node2) + else: + # we are comparing two revisions + deleted, unknown, ignored = [], [], [] + mf2 = mfmatches(ctx2) - if not compareworking: - # flush lists from dirstate before comparing manifests modified, added, clean = [], [], [] - - # make sure to sort the files so we talk to the disk in a - # reasonable order - getnode = lambda fn: mf1.get(fn, nullid) for fn in util.sort(mf2): if fn in mf1: - if (mf1.flags(fn) != mf2.flags(fn) or - (mf1[fn] != mf2[fn] and - (mf2[fn] != "" or fcmp(fn, getnode)))): + if ((mf1[fn] != mf2[fn] and + (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data()))) + or mf1.flags(fn) != mf2.flags(fn)): modified.append(fn) elif listclean: clean.append(fn) del mf1[fn] else: added.append(fn) - - removed = mf1.keys() + removed = util.sort(mf1.keys()) - # sort and return results: - for l in modified, added, removed, deleted, unknown, ignored, clean: - l.sort() - return (modified, added, removed, deleted, unknown, ignored, clean) + return modified, added, removed, deleted, unknown, ignored, clean def add(self, list): wlock = self.wlock()