Mercurial > public > mercurial-scm > hg-stable
diff mercurial/localrepo.py @ 7803:6d99ff7b79b5
Merge with stable
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Fri, 27 Feb 2009 08:13:42 -0600 |
parents | dd970a311ea8 b8d750daadde |
children | 4421abf8c85d |
line wrap: on
line diff
--- a/mercurial/localrepo.py Sun Jan 25 19:15:49 2009 +0100 +++ b/mercurial/localrepo.py Fri Feb 27 08:13:42 2009 -0600 @@ -10,7 +10,7 @@ import repo, changegroup import changelog, dirstate, filelog, manifest, context, weakref import lock, transaction, stat, errno, ui, store -import os, revlog, time, util, extensions, hook, inspect +import os, time, util, extensions, hook, inspect, error import match as match_ import merge as merge_ @@ -47,9 +47,9 @@ reqfile.write("%s\n" % r) reqfile.close() else: - raise repo.RepoError(_("repository %s not found") % path) + raise error.RepoError(_("repository %s not found") % path) elif create: - raise repo.RepoError(_("repository %s already exists") % path) + raise error.RepoError(_("repository %s already exists") % path) else: # find requirements requirements = [] @@ -57,7 +57,7 @@ requirements = self.opener("requires").read().splitlines() for r in requirements: if r not in self.supported: - raise repo.RepoError(_("requirement '%s' not supported") % r) + raise error.RepoError(_("requirement '%s' not supported") % r) except IOError, inst: if inst.errno != errno.ENOENT: raise @@ -88,6 +88,10 @@ def __getattr__(self, name): if name == 'changelog': self.changelog = changelog.changelog(self.sopener) + if 'HG_PENDING' in os.environ: + p = os.environ['HG_PENDING'] + if p.startswith(self.root): + self.changelog.readpending('00changelog.i.a') self.sopener.defversion = self.changelog.version return self.changelog if name == 'manifest': @@ -177,7 +181,7 @@ else: try: prevtags = self.filectx('.hgtags', parent).data() - except revlog.LookupError: + except error.LookupError: pass fp = self.wfile('.hgtags', 'wb') if prevtags: @@ -265,7 +269,7 @@ h.append(n) filetags[key] = (bin_n, h) - for k, nh in filetags.items(): + for k, nh in filetags.iteritems(): if k not in globaltags: globaltags[k] = nh tagtypes[k] = tagtype @@ -301,7 +305,7 @@ self.tagscache = {} self._tagstypecache = {} - for k,nh in globaltags.items(): + for k, nh in globaltags.iteritems(): n = nh[0] if n != nullid: self.tagscache[k] = n @@ -332,7 +336,7 @@ rev = c.rev() try: fnode = c.filenode('.hgtags') - except revlog.LookupError: + except error.LookupError: continue ret.append((rev, node, fnode)) if fnode in last: @@ -343,7 +347,7 @@ def tagslist(self): '''return a list of tags ordered by revision''' l = [] - for t, n in self.tags().items(): + for t, n in self.tags().iteritems(): try: r = self.changelog.rev(n) except: @@ -355,11 +359,12 @@ '''return the tags associated with a node''' if not self.nodetagscache: self.nodetagscache = {} - for t, n in self.tags().items(): + for t, n in self.tags().iteritems(): self.nodetagscache.setdefault(n, []).append(t) return self.nodetagscache.get(node, []) def _branchtags(self, partial, lrev): + # TODO: rename this function? tiprev = len(self) - 1 if lrev != tiprev: self._updatebranchcache(partial, lrev+1, tiprev+1) @@ -367,7 +372,7 @@ return partial - def branchtags(self): + def _branchheads(self): tip = self.changelog.tip() if self.branchcache is not None and self._branchcachetip == tip: return self.branchcache @@ -385,18 +390,38 @@ partial = self._ubranchcache self._branchtags(partial, lrev) + # this private cache holds all heads (not just tips) + self._ubranchcache = partial # the branch cache is stored on disk as UTF-8, but in the local # charset internally - for k, v in partial.items(): + for k, v in partial.iteritems(): self.branchcache[util.tolocal(k)] = v - self._ubranchcache = partial return self.branchcache + + def branchtags(self): + '''return a dict where branch names map to the tipmost head of + the branch, open heads come before closed''' + bt = {} + for bn, heads in self._branchheads().iteritems(): + head = None + for i in range(len(heads)-1, -1, -1): + h = heads[i] + if 'close' not in self.changelog.read(h)[5]: + head = h + break + # no open heads were found + if head is None: + head = heads[-1] + bt[bn] = head + return bt + + def _readbranchcache(self): partial = {} try: - f = self.opener("branch.cache") + f = self.opener("branchheads.cache") lines = f.read().split('\n') f.close() except (IOError, OSError): @@ -411,8 +436,8 @@ for l in lines: if not l: continue node, label = l.split(" ", 1) - partial[label.strip()] = bin(node) - except (KeyboardInterrupt, util.SignalInterrupt): + partial.setdefault(label.strip(), []).append(bin(node)) + except KeyboardInterrupt: raise except Exception, inst: if self.ui.debugflag: @@ -422,10 +447,11 @@ def _writebranchcache(self, branches, tip, tiprev): try: - f = self.opener("branch.cache", "w", atomictemp=True) + f = self.opener("branchheads.cache", "w", atomictemp=True) f.write("%s %s\n" % (hex(tip), tiprev)) - for label, node in branches.iteritems(): - f.write("%s %s\n" % (hex(node), label)) + for label, nodes in branches.iteritems(): + for node in nodes: + f.write("%s %s\n" % (hex(node), label)) f.rename() except (IOError, OSError): pass @@ -434,7 +460,12 @@ for r in xrange(start, end): c = self[r] b = c.branch() - partial[b] = c.node() + bheads = partial.setdefault(b, []) + bheads.append(c.node()) + for p in c.parents(): + pn = p.node() + if pn in bheads: + bheads.remove(pn) def lookup(self, key): if isinstance(key, int): @@ -460,7 +491,7 @@ key = hex(key) except: pass - raise repo.RepoError(_("unknown revision '%s'") % key) + raise error.RepoError(_("unknown revision '%s'") % key) def local(self): return True @@ -566,7 +597,7 @@ # abort here if the journal already exists if os.path.exists(self.sjoin("journal")): - raise repo.RepoError(_("journal already exists - run hg recover")) + raise error.RepoError(_("journal already exists - run hg recover")) # save dirstate for rollback try: @@ -637,7 +668,7 @@ def _lock(self, lockname, wait, releasefn, acquirefn, desc): try: l = lock.lock(lockname, 0, releasefn, desc=desc) - except lock.LockHeld, inst: + except error.LockHeld, inst: if not wait: raise self.ui.warn(_("waiting for lock on %s held by %r\n") % @@ -749,6 +780,8 @@ match=None, force=False, force_editor=False, p1=None, p2=None, extra={}, empty_ok=False): wlock = lock = None + if extra.get("close"): + force = True if files: files = util.unique(files) try: @@ -926,10 +959,13 @@ raise util.Abort(_("empty commit message")) text = '\n'.join(lines) + self.changelog.delayupdate() n = self.changelog.add(mn, changed + removed, text, trp, p1, p2, user, wctx.date(), extra) + p = lambda: self.changelog.writepending() and self.root or "" self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, - parent2=xp2) + parent2=xp2, pending=p) + self.changelog.finalize(trp) tr.close() if self.branchcache: @@ -1023,7 +1059,7 @@ wlock = self.wlock(False) for f in fixup: self.dirstate.normal(f) - except lock.LockException: + except lock.LockError: pass finally: del wlock @@ -1162,59 +1198,33 @@ finally: del wlock - def heads(self, start=None): + def heads(self, start=None, closed=True): heads = self.changelog.heads(start) + def display(head): + if closed: + return True + extras = self.changelog.read(head)[5] + return ('close' not in extras) # sort the output in rev descending order - heads = [(-self.changelog.rev(h), h) for h in heads] + heads = [(-self.changelog.rev(h), h) for h in heads if display(h)] return [n for (r, n) in util.sort(heads)] - def branchheads(self, branch=None, start=None): + def branchheads(self, branch=None, start=None, closed=True): if branch is None: branch = self[None].branch() - branches = self.branchtags() + branches = self._branchheads() if branch not in branches: return [] - # The basic algorithm is this: - # - # Start from the branch tip since there are no later revisions that can - # possibly be in this branch, and the tip is a guaranteed head. - # - # Remember the tip's parents as the first ancestors, since these by - # definition are not heads. - # - # Step backwards from the brach tip through all the revisions. We are - # guaranteed by the rules of Mercurial that we will now be visiting the - # nodes in reverse topological order (children before parents). - # - # If a revision is one of the ancestors of a head then we can toss it - # out of the ancestors set (we've already found it and won't be - # visiting it again) and put its parents in the ancestors set. - # - # Otherwise, if a revision is in the branch it's another head, since it - # wasn't in the ancestor list of an existing head. So add it to the - # head list, and add its parents to the ancestor list. - # - # If it is not in the branch ignore it. - # - # Once we have a list of heads, use nodesbetween to filter out all the - # heads that cannot be reached from startrev. There may be a more - # efficient way to do this as part of the previous algorithm. - - set = util.set - heads = [self.changelog.rev(branches[branch])] - # Don't care if ancestors contains nullrev or not. - ancestors = set(self.changelog.parentrevs(heads[0])) - for rev in xrange(heads[0] - 1, nullrev, -1): - if rev in ancestors: - ancestors.update(self.changelog.parentrevs(rev)) - ancestors.remove(rev) - elif self[rev].branch() == branch: - heads.append(rev) - ancestors.update(self.changelog.parentrevs(rev)) - heads = [self.changelog.node(rev) for rev in heads] + bheads = branches[branch] + # the cache returns heads ordered lowest to highest + bheads.reverse() if start is not None: - heads = self.changelog.nodesbetween([start], heads)[2] - return heads + # filter out the heads that cannot be reached from startrev + bheads = self.changelog.nodesbetween([start], bheads)[2] + if not closed: + bheads = [h for h in bheads if + ('close' not in self.changelog.read(h)[5])] + return bheads def branches(self, nodes): if not nodes: @@ -1393,7 +1403,8 @@ # sanity check our fetch list for f in fetch.keys(): if f in m: - raise repo.RepoError(_("already have changeset ") + short(f[:4])) + raise error.RepoError(_("already have changeset ") + + short(f[:4])) if base.keys() == [nullid]: if force: @@ -1579,7 +1590,7 @@ if self.ui.verbose or source == 'bundle': self.ui.status(_("%d changesets found\n") % len(nodes)) if self.ui.debugflag: - self.ui.debug(_("List of changesets:\n")) + self.ui.debug(_("list of changesets:\n")) for node in nodes: self.ui.debug("%s\n" % hex(node)) @@ -1756,7 +1767,7 @@ # we only need to see a diff. deltamf = mnfst.readdelta(mnfstnode) # For each line in the delta - for f, fnode in deltamf.items(): + for f, fnode in deltamf.iteritems(): f = changedfiles.get(f, None) # And if the file is in the list of files we care # about. @@ -2030,9 +2041,6 @@ revisions += len(fl) - o files += 1 - # make changelog see real files again - cl.finalize(trp) - newheads = len(self.changelog.heads()) heads = "" if oldheads and newheads != oldheads: @@ -2043,9 +2051,13 @@ % (changesets, revisions, files, heads)) if changesets > 0: + p = lambda: self.changelog.writepending() and self.root or "" self.hook('pretxnchangegroup', throw=True, node=hex(self.changelog.node(cor+1)), source=srctype, - url=url) + url=url, pending=p) + + # make changelog see real files again + cl.finalize(trp) tr.close() finally: @@ -2075,7 +2087,7 @@ try: resp = int(l) except ValueError: - raise util.UnexpectedOutput( + raise error.ResponseError( _('Unexpected response from remote server:'), l) if resp == 1: raise util.Abort(_('operation forbidden by server')) @@ -2088,7 +2100,7 @@ try: total_files, total_bytes = map(int, l.split(' ', 1)) except (ValueError, TypeError): - raise util.UnexpectedOutput( + raise error.ResponseError( _('Unexpected response from remote server:'), l) self.ui.status(_('%d files to transfer, %s of data\n') % (total_files, util.bytecount(total_bytes))) @@ -2100,7 +2112,7 @@ name, size = l.split('\0', 1) size = int(size) except (ValueError, TypeError): - raise util.UnexpectedOutput( + raise error.ResponseError( _('Unexpected response from remote server:'), l) self.ui.debug(_('adding %s (%s)\n') % (name, util.bytecount(size))) ofp = self.sopener(name, 'w')