Re: [PATCH 10 of 12 V3] bundle2: support a 'records' mode for the 'bookmarks' part
Potential bug mentioned inline On 11/20/17 8:52 AM, Boris Feld wrote: # HG changeset patch # User Boris Feld# Date 1508246776 -7200 # Tue Oct 17 15:26:16 2017 +0200 # Node ID 7e610585998e3eff1d0498a6ce024350bb04fc23 # Parent ab0fe3f91e7b9ed7bd508a9affa0b91128facf13 # EXP-Topic b2.bookmarks # Available At https://urldefense.proofpoint.com/v2/url?u=https-3A__bitbucket.org_octobus_mercurial-2Ddevel_=DwIGaQ=5VD0RTtNlTh3ycd41b3MUw=nuarHzhP1wi1T9iURRCj1A=EyaOozG7mqYFV7-uTtZuxV3zYCWicwAVyqzUZmeNyFQ=6vm3KUlcZ0dR7UkeLmcuPjYetLgy50kNjAlNrys3aEI= # hg pull https://urldefense.proofpoint.com/v2/url?u=https-3A__bitbucket.org_octobus_mercurial-2Ddevel_=DwIGaQ=5VD0RTtNlTh3ycd41b3MUw=nuarHzhP1wi1T9iURRCj1A=EyaOozG7mqYFV7-uTtZuxV3zYCWicwAVyqzUZmeNyFQ=6vm3KUlcZ0dR7UkeLmcuPjYetLgy50kNjAlNrys3aEI= -r 7e610585998e bundle2: support a 'records' mode for the 'bookmarks' part In this mode, the bookmarks changes are record in the 'bundleoperation' records instead of inflicted to the repository. This is necessary to use the part when pulling. diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -1898,40 +1898,55 @@ def handlepushkey(op, inpart): def handlebookmark(op, inpart): """transmit bookmark information -The part contains binary encoded bookmark information. The bookmark -information is applied as is to the unbundling repository. Make sure a -'check:bookmarks' part is issued earlier to check for race condition in -such update. +The part contains binary encoded bookmark information. + +The exact behavior of this part can be controlled by the 'bookmarks' mode +on the bundle operation. -This behavior is suitable for pushing. Semantic adjustment will be needed -for pull. +When mode is 'apply' (the default) the bookmark information is applied as +is to the unbundling repository. Make sure a 'check:bookmarks' part is +issued earlier to check for push races in such update. This behavior is +suitable for pushing. + +When mode is 'records', the information is recorded into the 'bookmarks' +records of the bundle operation. This behavior is suitable for pulling. """ changes = bookmarks.binarydecode(inpart) -tr = op.gettransaction() -bookstore = op.repo._bookmarks +pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat') +bookmarksmode = op.modes.get('bookmarks', 'apply') -pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat') -if pushkeycompat: -allhooks = [] +if bookmarksmode == 'apply': +tr = op.gettransaction() +bookstore = op.repo._bookmarks +if pushkeycompat: +allhooks = [] +for book, node in changes: +hookargs = tr.hookargs.copy() +hookargs['pushkeycompat'] = '1' +hookargs['namespace'] = 'bookmark' +hookargs['key'] = book +hookargs['old'] = nodemod.hex(bookstore.get(book, '')) +hookargs['new'] = nodemod.hex(node if node is not None else '') +allhooks.append(hookargs) + +for hookargs in allhooks: +op.repo.hook('prepushkey', throw=True, **hookargs) + +bookstore.applychanges(op.repo, op.gettransaction(), changes) + +if pushkeycompat: +def runhook(): +for hookargs in allhooks: +op.repo.hook('prepushkey', **hookargs) Should this be 'pushkey' instead of 'prepushkey'? Since it happens after the lock? That would match localrepository.pushkey()'s behavior. +op.repo._afterlock(runhook) + +elif bookmarksmode == 'records': for book, node in changes: -hookargs = tr.hookargs.copy() -hookargs['pushkeycompat'] = '1' -hookargs['namespace'] = 'bookmark' -hookargs['key'] = book -hookargs['old'] = nodemod.hex(bookstore.get(book, '')) -hookargs['new'] = nodemod.hex(node if node is not None else '') -allhooks.append(hookargs) -for hookargs in allhooks: -op.repo.hook('prepushkey', throw=True, **hookargs) - -bookstore.applychanges(op.repo, tr, changes) - -if pushkeycompat: -def runhook(): -for hookargs in allhooks: -op.repo.hook('prepushkey', **hookargs) -op.repo._afterlock(runhook) +record = {'bookmark': book, 'node': node} +op.records.add('bookmarks', record) +else: +raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode) @parthandler('phase-heads') def handlephases(op, inpart): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org
D2108: infinitepush: drop the `--to` flag to push and use `-B` instead
durham added a comment. > There are things which I am not sure whether to keep or not: > > - the --bundle-store flag to push command This is useful for scripts or tools that want to upload a commit to the cloud without having to give it a name. For instance, you can use it to push a commit then send that commit hash to some build service which can checkout the commit without having to worry about a bookmark name. But this could always be added back later, so it's probably fine to drop it if there's not an immediate need in Mozilla's use case. > - functionality to pull from bundlestore using hg pull Similar to the points above and below, this is useful for automation that already passes hashes around. Not having to pass around bookmark names as well means it's easier for that automation to migrate to infinitepush. > - functionality to pull changesets from bundlestore if a changeset is not found locally on hg update This is a bit of magic that user's really like. When combined with automatic backup pushes, it makes it feel like everyone is using the same repository. I'd highly recommend keeping this just for the eventual PR of saying "I can just hg commit, and my friend can do hg checkout HASH" > - logic around sql store Without this, would the server always store data in the filesystem? The sql store seems like an important bit of making this robust in enterprise usage. > - interaction with the hoisting functionality of remotenames extension which is also being moved to core I'm not familiar with how infinitepush plays into hoisting, but I just wanted to make sure users never have to type 'remote/' or 'default/'. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2108 To: pulkit, #hg-reviewers, indygreg Cc: indygreg, durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1814: rebase: add experimental.inmemory.nomergedriver to turn off IMM
durham added inline comments. INLINE COMMENTS > rebase.py:812 > +elif (ui.config('experimental', 'mergedriver') and > + ui.configbool('rebase', > 'experimental.inmemory.nomergedriver')): > +whynotimm = 'mergedriver enabled' I think we probably want IMM disabled by default if merge drivers are specified. If the merge-driver does IO during the preprocess step, it could cause problems in an IMM situation. So I don't think we can enable IMM by default in the merge-driver case until merge-driver enforces (or at least documents) that there should be no IO during preprocess. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1814 To: phillco, #hg-reviewers Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1806: filemerge: fix backing up an in-memory file to a custom location
durham accepted this revision. durham added a comment. Accepting, since it seems more correct than before. Should we even be calling _makebackup in the case of an inmemory merge? Like, maybe the makebackup should be conditional based on if the source file context is actually a workingctx? INLINE COMMENTS > filemerge.py:623 > +Backups are only need to be written for the premerge, and not again > during > +the main merge. > """ I know you're just documenting the parameter that already existed, but might be nice to explain why this is (if you happen to know why right now). > filemerge.py:643 > +if premerge: > +# Otherwise, write to wherever path the user specified the > backups > +# should go. We still need to switch based on whether the source > is s/wherever/whatever/ REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1806 To: phillco, #hg-reviewers, durham Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1564: worker: make windows workers daemons
durham added inline comments. INLINE COMMENTS > worker.py:286 > +threads.remove(t) > +except KeyboardInterrupt: > +trykillworkers() Why only do it on keyboard interrupt? What if there's another exception? If you did it for all exceptions, you could drop the trykillworkers() inside the loop, and just throw the exception up to here. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1564 To: wlis, #hg-reviewers, ikostia Cc: durham, ikostia, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D821: unamend: move fb extension unamend to core
durham added inline comments. INLINE COMMENTS > pulkit wrote in uncommit.py:265 > Okay while trying to add this condition, I found we cannot refuse to unamend > a changeset on the basis of unamend_source, for e.g > `a -amend-> b -unamend-> a' -amend-> c -unamend-> a''` > > But if we refuse on basis on unamend_source, unamend `c` will refuse. We need > to be more smart here but when I think the other way around, I think it's > okay to unamend an unamend. I am open to suggestion and will go with what you > guys prefer is good. I'd just let unamend undo an unamend. Letting unamend toggle back and forth between the two states seems like it might grant the user more confidence in the command, even. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D821 To: pulkit, #hg-reviewers, durham Cc: quark, durin42, ryanmce, singhsrb, durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1358: remotenames: store journal entry for bookmarks if journal is loaded
durham added a comment. There seems like two use cases for remotenames: knowledge about the most recently seen location of each remote bookmark, and knowledge about where the remote bookmarks have been over time. I think 99% of the benefit of remotenames comes from the first part. Building a storage layer which suits both the journal and remotenames seems like massive scope creep to address the 1% use case. Even if we did unify the storage models, I still don't believe remotenames and the journal could share a common storage format because they don't have similar access patterns. Remotenames is much more of a random access dict-like object, while the journal is much more of a sequential log. If I want to lookup the value of the latest remote master I don't want to deal with scanning some log of recent activity, and if I want to maintain a log of recent activity I don't want to build an index that allows for random access. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1358 To: pulkit, #hg-reviewers Cc: durham, quark, durin42, smf, dlax, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: Upstreaming Facebook extensions
On 11/14/17 8:26 AM, Augie Fackler wrote: On Nov 13, 2017, at 22:49, Matt Harbisonwrote: On Mon, 13 Nov 2017 22:12:31 -0500, Jun Wu wrote: Excerpts from Matt Harbison's message of 2017-11-13 21:50:29 -0500: For LFS, it has been used in a repo synced from p4 for about half a year. It's mostly good except for lack of features (ex. support the Git-LFS SSH authentication, support gc, etc.). It was actually written with some extra care of upstream-friendliness. For example, I put remotefilelog integration in remotefilelog instead of LFS intentionally. Help is definitely welcomed! OK, good to know. So is upstreaming lfs in a single patch and marking it experimental a reasonable next step, or does this need to incubate in hg-experimental a bit more? I didn't get it working outside of the tests (it said something about not having a common changegroup version), but it looks like there's some low hanging fruit like registering the config options, and tracking down some warning in url.py about an "unquoted realm" or similar. I know BC doesn't apply to experimental things, but realistically, I assume things won't need to change other that maybe config stuff to add features? I wouldn't mind using this sooner rather than later if the files can always be retrieved. My sense of lfs is that it's a much better overall approach to largefiles handling, and that we should probably work to get it in core sooner rather than later as long as Facebook is ready to stabilize its development somewhat. When we do that, the commit message should go into detail why we think lfs is better than largefiles, and then probably also add a note to the largefiles help that we think lfs is the way of the future. Does that sound like a starting place? Facebook folks, do you think it's a reasonable time to land lfs in core? David should chime in here, because I haven't really been watching the LFS side of things. I believe it's being used in production with a number of users at least, and I haven't heard of any lfs specific issues, so my guess is it's probably in good shape for upstreaming. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1431: sshpeer: making the ssh error message configurable
durham added a comment. I'd also update the commit summary with an example of what a better message might be. Like "there was an ssh error, please see http://company/internalwiki/ssh.html; REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1431 To: zuza, #hg-reviewers, durham, mitrandir Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1431: sshpeer: making the ssh error message configurable
durham requested changes to this revision. durham added inline comments. This revision now requires changes to proceed. INLINE COMMENTS > sshpeer.py:210 > +"errormessage", > +_("no suitable response from remote hg" > The standard Mercurial way to format this would be something like: msg = self.ui.config("ssh", "errormessage", _("no suitable response from remote hg")) self._abort(error.RepoError(msg)) > test-ssh.t:314 > + > + > clone bookmarks These lines seem unrelated to your change and therefore probably shouldn't have changed. Usually this is caused by your editor trying to remove trailing whitespace from a file, then run-tests -i adding the missing new line back in. Can you undo these new line changes? 'hg uncommit tests/test-ssh.t && hg commit -i' could make it pretty straightforward. > test-ssh.t:447 >> from mercurial import exchange, extensions > - > >> def wrappedpush(orig, repo, *args, **kwargs): Same for this type of change REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1431 To: zuza, #hg-reviewers, durham, mitrandir Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1358: remotenames: store journal entry for bookmarks if journal is loaded
durham added a comment. I'm a little confused here, in the same way Pulkit has expressed. remotenames has a couple parts related to storage: one is the storing of the remotenames themselves, and the other is tracking how they change over time. The remotename storage itself doesn't seem like it overlaps with journal or blackbox, as it's not a log. The tracking of how they change over time seems to fit the journal exactly, which is a store for tracking how various references to commits (working copy parent, bookmarks, and now remotenames) change over time. So I guess I'm confused about what part of remotenames needs unification? They seem to either be completely orthogonal, or a complete match for an existing system. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1358 To: pulkit, #hg-reviewers Cc: durham, quark, durin42, smf, dlax, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D821: unamend: move fb extension unamend to core
durham accepted this revision. durham added a comment. Overall looks good to me. My one comment is probably not enough to block this going in. INLINE COMMENTS > uncommit.py:260 > +prednode = markers[0].prednode() > +predctx = unfi[prednode] > + Might be worth doing the predecessor check in the lock as well, since the result of this verification could technically change between now and when the lock is taken. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D821 To: pulkit, #hg-reviewers, durham Cc: ryanmce, singhsrb, durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1329: bundle: allow bundlerepo to support alternative manifest implementations
This revision was automatically updated to reflect the committed changes. Closed by commit rHGa2dfc723b6b5: bundle: allow bundlerepo to support alternative manifest implementations (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1329?vs=3314=3385 REVISION DETAIL https://phab.mercurial-scm.org/D1329 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -352,14 +352,31 @@ self.filestart = self.bundle.tell() return m +def _consumemanifest(self): +"""Consumes the manifest portion of the bundle, setting filestart so the +file portion can be read.""" +self.bundle.seek(self.manstart) +self.bundle.manifestheader() +for delta in self.bundle.deltaiter(): +pass +self.filestart = self.bundle.tell() + @localrepo.unfilteredpropertycache def manstart(self): self.changelog return self.manstart @localrepo.unfilteredpropertycache def filestart(self): self.manifestlog + +# If filestart was not set by self.manifestlog, that means the +# manifestlog implementation did not consume the manifests from the +# changegroup (ex: it might be consuming trees from a separate bundle2 +# part instead). So we need to manually consume it. +if 'filestart' not in self.__dict__: +self._consumemanifest() + return self.filestart def url(self): To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1329: bundle: allow bundlerepo to support alternative manifest implementations
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY With our treemanifest logic, the manifests are no longer transported as part of the changegroup and are no longer stored in a revlog. This means the self.manifestlog line in bundlerepo.filestart no longer calls _constructmanifest, and therefore does not consume the manifest portion of the changegroup, which means filestart is not populated and we result in an infinite loop. The fix is to make filestart aware that self.manifestlog might not consume the changegroup part, and consume it manually if necessary. There's currently no way to test this in core, but our treemanifest extension has tests to cover this. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1329 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -352,14 +352,31 @@ self.filestart = self.bundle.tell() return m +def _consumemanifest(self): +"""Consumes the manifest portion of the bundle, setting filestart so the +file portion can be read.""" +self.bundle.seek(self.manstart) +self.bundle.manifestheader() +for delta in self.bundle.deltaiter(): +pass +self.filestart = self.bundle.tell() + @localrepo.unfilteredpropertycache def manstart(self): self.changelog return self.manstart @localrepo.unfilteredpropertycache def filestart(self): self.manifestlog + +# If filestart was not set by self.manifestlog, that means the +# manifestlog implementation did not consume the manifests from the +# changegroup (ex: it might be consuming trees from a separate bundle2 +# part instead). So we need to manually consume it. +if 'filestart' not in self.__dict__: +self._consumemanifest() + return self.filestart def url(self): To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: Storage format for remotenames.
I wish we had some easily reusable serializer/deserializer instead of having to reinvent these every time. What's our reasoning for not using json? I forget. If there are some weird characters, like control characters or something, that break json, I'd say we just use json and prevent users from creating bookmarks and paths with those names. Otherwise we could move the paths to a separate section and format the file something like: [paths] 1=ssh://blahblah.com//blahblah 2=ssh://blahblah.com//blahblah 3=ssh://blahblah.com//blahblah [bookmarks] HASH 2 book/mark/name HASH 1 book2/foo/bar [branches] HASH 1 HASH 3 On 11/6/17 4:34 AM, Pulkit Goyal wrote: Hey, I am working on porting functionalities from hgremotenames extension to core. The hgremotenames extensions pull the information about remote branches and remote bookmarks and store them to provide a better workflow. The current storage format which hgremotenames has is having a file `.hg/remotenames` in which each line is of the format `node nametype name`, where - `node` refers to node where the remotename was last seen - `nametype` refers whether it's a bookmark or branch - `name` consists of name of the remote and name of the remote bookmark/branch At sprint, Ryan suggested to split the file according to bookmark and branches so that we can read and write more easily which makes sense. While working on the patches, I found out that if the name of the remote contains a '/', then the current storage format is not good and we can fail to parse things correctly. Do you guys have any better ideas on how we can store remotenames? Pulkit ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1257: dirstate: remove excess attribute lookups for dirstate.status (issue5714)
This revision was automatically updated to reflect the committed changes. Closed by commit rHGffeea2406276: dirstate: remove excess attribute lookups for dirstate.status (issue5714) (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1257?vs=3147=3150 REVISION DETAIL https://phab.mercurial-scm.org/D1257 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -1053,6 +1053,9 @@ removed, deleted, clean = [], [], [] dmap = self._map +dmap.preload() +dcontains = dmap.__contains__ +dget = dmap.__getitem__ ladd = lookup.append# aka "unsure" madd = modified.append aadd = added.append @@ -1074,7 +1077,7 @@ full = listclean or match.traversedir is not None for fn, st in self.walk(match, subrepos, listunknown, listignored, full=full).iteritems(): -if fn not in dmap: +if not dcontains(fn): if (listignored or mexact(fn)) and dirignore(fn): if listignored: iadd(fn) @@ -1089,7 +1092,7 @@ # a list, but falls back to creating a full-fledged iterator in # general. That is much slower than simply accessing and storing the # tuple members one by one. -t = dmap[fn] +t = dget(fn) state = t[0] mode = t[1] size = t[2] @@ -1216,8 +1219,8 @@ return self.copymap def clear(self): -self._map = {} -self.copymap = {} +self._map.clear() +self.copymap.clear() self.setparents(nullid, nullid) def iteritems(self): @@ -1247,6 +1250,10 @@ def keys(self): return self._map.keys() +def preload(self): +"""Loads the underlying data, if it's not already loaded""" +self._map + def nonnormalentries(self): '''Compute the nonnormal dirstate entries from the dmap''' try: @@ -1373,6 +1380,13 @@ if not self._dirtyparents: self.setparents(*p) +# Avoid excess attribute lookups by fast pathing certain checks +self.__contains__ = self._map.__contains__ +self.__getitem__ = self._map.__getitem__ +self.__setitem__ = self._map.__setitem__ +self.__delitem__ = self._map.__delitem__ +self.get = self._map.get + def write(self, st, now): st.write(parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)) To: durham, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1257: dirstate: remove excess attribute lookups for dirstate.status (issue5714)
durham added a comment. Fixed by using _map.clear() instead of replacing the _map. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1257 To: durham, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1257: dirstate: remove excess attribute lookups for dirstate.status (issue5714)
durham updated this revision to Diff 3147. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1257?vs=3136=3147 REVISION DETAIL https://phab.mercurial-scm.org/D1257 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -1053,6 +1053,9 @@ removed, deleted, clean = [], [], [] dmap = self._map +dmap.preload() +dcontains = dmap.__contains__ +dget = dmap.__getitem__ ladd = lookup.append# aka "unsure" madd = modified.append aadd = added.append @@ -1074,7 +1077,7 @@ full = listclean or match.traversedir is not None for fn, st in self.walk(match, subrepos, listunknown, listignored, full=full).iteritems(): -if fn not in dmap: +if not dcontains(fn): if (listignored or mexact(fn)) and dirignore(fn): if listignored: iadd(fn) @@ -1089,7 +1092,7 @@ # a list, but falls back to creating a full-fledged iterator in # general. That is much slower than simply accessing and storing the # tuple members one by one. -t = dmap[fn] +t = dget(fn) state = t[0] mode = t[1] size = t[2] @@ -1216,8 +1219,8 @@ return self.copymap def clear(self): -self._map = {} -self.copymap = {} +self._map.clear() +self.copymap.clear() self.setparents(nullid, nullid) def iteritems(self): @@ -1247,6 +1250,10 @@ def keys(self): return self._map.keys() +def preload(self): +"""Loads the underlying data, if it's not already loaded""" +self._map + def nonnormalentries(self): '''Compute the nonnormal dirstate entries from the dmap''' try: @@ -1373,6 +1380,13 @@ if not self._dirtyparents: self.setparents(*p) +# Avoid excess attribute lookups by fast pathing certain checks +self.__contains__ = self._map.__contains__ +self.__getitem__ = self._map.__getitem__ +self.__setitem__ = self._map.__setitem__ +self.__delitem__ = self._map.__delitem__ +self.get = self._map.get + def write(self, st, now): st.write(parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)) To: durham, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1253: dirstate: avoid reading the map when possible (issue5713) (issue5717)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG6e66033f91cc: dirstate: avoid reading the map when possible (issue5713) (issue5717) (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1253?vs=3129=3146 REVISION DETAIL https://phab.mercurial-scm.org/D1253 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -129,7 +129,7 @@ def _map(self): '''Return the dirstate contents as a map from filename to (state, mode, size, time).''' -self._read() +self._map = dirstatemap(self._ui, self._opener, self._root) return self._map @property @@ -353,10 +353,6 @@ f.discard() raise -def _read(self): -self._map = dirstatemap(self._ui, self._opener, self._root) -self._map.read() - def invalidate(self): '''Causes the next access to reread the dirstate. @@ -1201,14 +1197,24 @@ self._root = root self._filename = 'dirstate' -self._map = {} -self.copymap = {} self._parents = None self._dirtyparents = False # for consistent view between _pl() and _read() invocations self._pendingmode = None +@propertycache +def _map(self): +self._map = {} +self.read() +return self._map + +@propertycache +def copymap(self): +self.copymap = {} +self._map +return self.copymap + def clear(self): self._map = {} self.copymap = {} @@ -1388,7 +1394,7 @@ @propertycache def identity(self): -self.read() +self._map return self.identity @propertycache To: durham, #hg-reviewers, lothiraldan, yuja Cc: yuja, lothiraldan, mharbison72, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1252: dirstate: move clear onto dirstatemap class
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0217f75b6e59: dirstate: move clear onto dirstatemap class (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1252?vs=3128=3145 REVISION DETAIL https://phab.mercurial-scm.org/D1252 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -593,8 +593,7 @@ return path def clear(self): -self._map = dirstatemap(self._ui, self._opener, self._root) -self._map.setparents(nullid, nullid) +self._map.clear() self._lastnormaltime = 0 self._updatedfiles.clear() self._dirty = True @@ -1210,6 +1209,11 @@ # for consistent view between _pl() and _read() invocations self._pendingmode = None +def clear(self): +self._map = {} +self.copymap = {} +self.setparents(nullid, nullid) + def iteritems(self): return self._map.iteritems() To: durham, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1257: dirstate: remove excess attribute lookups for dirstate.status (issue5714)
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY A recent refactor added a layer of abstraction to the dirstate which makes doing things like 'foo in dirstate' now require some extra Python attribute lookups. This is causing a 100ms slow down in hg status for mozilla-central. The fix is to hoist the inner dict's functions onto the main class once the lazy loading it complete, as well as store the actual functions before doing the status loop (as is done for other such functions). In my testing, it seems to address the performance regression, but we'll need to see the perf run results to know for sure. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1257 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -1053,6 +1053,9 @@ removed, deleted, clean = [], [], [] dmap = self._map +dmap.preload() +dcontains = dmap.__contains__ +dget = dmap.__getitem__ ladd = lookup.append# aka "unsure" madd = modified.append aadd = added.append @@ -1074,7 +1077,7 @@ full = listclean or match.traversedir is not None for fn, st in self.walk(match, subrepos, listunknown, listignored, full=full).iteritems(): -if fn not in dmap: +if not dcontains(fn): if (listignored or mexact(fn)) and dirignore(fn): if listignored: iadd(fn) @@ -1089,7 +1092,7 @@ # a list, but falls back to creating a full-fledged iterator in # general. That is much slower than simply accessing and storing the # tuple members one by one. -t = dmap[fn] +t = dget(fn) state = t[0] mode = t[1] size = t[2] @@ -1247,6 +1250,10 @@ def keys(self): return self._map.keys() +def preload(self): +"""Loads the underlying data, if it's not already loaded""" +self._map + def nonnormalentries(self): '''Compute the nonnormal dirstate entries from the dmap''' try: @@ -1373,6 +1380,13 @@ if not self._dirtyparents: self.setparents(*p) +# Avoid excess attribute lookups by fast pathing certain checks +self.__contains__ = self._map.__contains__ +self.__getitem__ = self._map.__getitem__ +self.__setitem__ = self._map.__setitem__ +self.__delitem__ = self._map.__delitem__ +self.get = self._map.get + def write(self, st, now): st.write(parsers.pack_dirstate(self._map, self.copymap, self.parents(), now)) To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1144: directaccess: add support to export and tests to demonstrate things
durham added a comment. @lothiraldan Are pinned revs deleted when the cache is rebuilt? I would kinda not expect them to be. If they are deleted, then yea moving these exceptions to another attribute that is persisted across cache clears makes sense. For your concern about changing from a command-level option to a scope and revrange level option, I have a couple comments: - If we went the current command-level flag, the examples your talking about where it's initially ambiguous if the command is actually reading or writing the hidden commit (phase, fold --reuse-msg, etc) would default to warn mode and the only negative effect would be the user gets an extra warning message telling them a hash they passed is hidden. Probably not a common occurrence, and not a huge issue. In the future we could add more logic within the command to suppress that warning when doing the read only paths, but for the short term this let's us get directaccess out the door without auditing and updating every commands logic. - As for adding exception context manager type things, I think one of the issues with our current visibility code is that it requires the person writing a command to be too aware that visibility exists. If we start requiring commands to use this new context appropriately, and to pass the right argument to revrange at the right time, I think we just introduce more risk of commands being written incorrectly and having more complexity at the business logic layer. If we can avoid all of that at the cost of printing warnings to the user slightly more often, I think that's a good trade off. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1144 To: pulkit, #hg-reviewers, lothiraldan Cc: durham, dlax, lothiraldan, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1201: dirstate: clean up when restoring identical backups
durham accepted this revision. durham added a comment. lgtm. Is this a recent regression? Seems like this would be polluting lots of peoples repositories. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1201 To: mbthomas, #hg-reviewers, krbullock, durham Cc: krbullock, durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1253: dirstate: avoid reading the map when possible (issue5713) (issue5717)
durham added a comment. @mharbison72 your test output looks like the sort order changed? Does it consistently repro before and after this patch? The sort order is from the sort -u in the test, so I can't imagine this affecting it. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1253 To: durham, #hg-reviewers, lothiraldan Cc: yuja, lothiraldan, mharbison72, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1253: dirstate: avoid reading the map when possible (issue5713) (issue5717)
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Before the recent refactor, we would not load the entire map until it was accessed. As part of the refactor, that got lost and even just trying to load the dirstate parents would load the whole map. This caused a perf regression (issue5713) and a regression with static http serving (issue5717). Making it lazy loaded again fixes both. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1253 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -129,7 +129,7 @@ def _map(self): '''Return the dirstate contents as a map from filename to (state, mode, size, time).''' -self._read() +self._map = dirstatemap(self._ui, self._opener, self._root) return self._map @property @@ -353,10 +353,6 @@ f.discard() raise -def _read(self): -self._map = dirstatemap(self._ui, self._opener, self._root) -self._map.read() - def invalidate(self): '''Causes the next access to reread the dirstate. @@ -1201,14 +1197,24 @@ self._root = root self._filename = 'dirstate' -self._map = {} -self.copymap = {} self._parents = None self._dirtyparents = False # for consistent view between _pl() and _read() invocations self._pendingmode = None +@propertycache +def _map(self): +self._map = {} +self.read() +return self._map + +@propertycache +def copymap(self): +self.copymap = {} +self._map +return self.copymap + def clear(self): self._map = {} self.copymap = {} @@ -1388,7 +1394,7 @@ @propertycache def identity(self): -self.read() +self._map return self.identity @propertycache To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1252: dirstate: move clear onto dirstatemap class
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY A future diff will move the lazy loading aspect of dirstate to the dirstatemap class. This means it requires a slightly different strategy of clearing than just reinstantiating the object (since just reinstantiating the object will lazily load the on disk data again later instead of remaining permanently empty). So let's give it it's own clear function. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1252 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -593,8 +593,7 @@ return path def clear(self): -self._map = dirstatemap(self._ui, self._opener, self._root) -self._map.setparents(nullid, nullid) +self._map.clear() self._lastnormaltime = 0 self._updatedfiles.clear() self._dirty = True @@ -1210,6 +1209,11 @@ # for consistent view between _pl() and _read() invocations self._pendingmode = None +def clear(self): +self._map = {} +self.copymap = {} +self.setparents(nullid, nullid) + def iteritems(self): return self._map.iteritems() To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: Deploying registrar configs in a BC safe way
On 10/25/17 5:48 AM, Augie Fackler wrote: On Oct 25, 2017, at 07:51, Yuya Nishihara <y...@tcha.org> wrote: On Tue, 24 Oct 2017 18:49:50 -0700, Gregory Szorc wrote: On Oct 24, 2017, at 10:25, Durham Goode <dur...@fb.com> wrote: I've been trying to update our extensions to support the new registrar class, and one thing I've noticed is that it's not really possible to have code that works with old versions of mercurial and new versions of mercurial at the same time, without having devel warnings. I can easily stub out the registration logic if the registrar module doesn't exist, but I can't leave the default values on the actual config accessor lines, which old mercurial versions would need. I made all extension-specific configs "dynamic", and add explicit defaults to ui.config*() calls instead. https://urldefense.proofpoint.com/v2/url?u=https-3A__bitbucket.org_yuja_hgext-2Dtextful_commits_1f284a246abb=DwIFAg=5VD0RTtNlTh3ycd41b3MUw=nuarHzhP1wi1T9iURRCj1A=lR7l4BfqDK6q7Iea-79Q-h1teNA3oQLKKh7QiqDoA4M=2ycQgVlPv5dGVFQUeMGUTS1h8KHBikUl11j9FoGk7m4= I tried to think of some way to fix this, like by leaving the defaults on the config access line, but ignoring it somehow, but that sounds funky. Thoughts on how to fix this? How about making registrar instances on extensions have different behavior from core? e.g. * No devel warnings for e.g. missing default values for a few releases * Arguments to control which devel warnings are issued Or, * No devel warnings for the "ui.config()" default matching the "configitem" default, which should be harmless That seems like a good transitional step, I like it. What do others think? That would work for me. Adding the register's isn't the painful or risky part, updating the defaults and refactoring to support old mercurial is. So this would address that issue nicely. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH remotenames] configs: update configs to use registrar
I didn't fill in the commit message on this one. V2 incoming On 10/24/17 2:15 PM, Durham Goode wrote: # HG changeset patch # User Durham Goode <dur...@fb.com> # Date 1508879615 25200 # Tue Oct 24 14:13:35 2017 -0700 # Node ID 9ead5c33dd22a9a66cea009bcf76ffd801d5677e # Parent 5fb97b6b3d46fcd4dc1b3cb6be137762eb9e60e7 configs: update configs to use registrar diff --git a/remotenames.py b/remotenames.py --- a/remotenames.py +++ b/remotenames.py @@ -32,12 +32,14 @@ from mercurial import localrepo from mercurial import lock as lockmod from mercurial import namespaces from mercurial import obsutil +from mercurial import registrar from mercurial import repair from mercurial import repoview from mercurial import revset from mercurial import scmutil from mercurial import setdiscovery from mercurial import templatekw +from mercurial import ui as uimod from mercurial import url from mercurial import util from mercurial import vfs as vfsmod @@ -51,12 +53,64 @@ try: except ImportError: smartset = revset +configtable = {} +try: +from mercurial import registrar +if not util.safehasattr(registrar, 'configitem'): +raise ImportError() +# Supported in 4.3+ +configitem = registrar.configitem(configtable) +except ImportError: +# Support older releases that didn't register config defaults +registrar = None +def configitem(section, name, default=None): +configtable[(section, name)] = default + +def configdefault(orig, self, section, name, default=None, + untrusted=False): +if default is None: +default = configtable.get((section, name)) +return orig(self, section, name, default=default, untrusted=untrusted) + +configitem('remotenames', 'alias.default', default=False) +configitem('remotenames', 'allownonfastforward', default=False) +configitem('remotenames', 'bookmarks', default=True) +configitem('remotenames', 'branches', default=True) +configitem('remotenames', 'calculatedistance', default=True) +configitem('remotenames', 'disallowedbookmarks', default=[]) +configitem('remotenames', 'disallowedhint', default=None) +configitem('remotenames', 'disallowedto', default=None) +configitem('remotenames', 'fastheaddiscovery', default=True) +configitem('remotenames', 'forcecompat', default=False) +configitem('remotenames', 'forceto', default=False) +configitem('remotenames', 'hoist', default='default') +configitem('remotenames', 'precachecurrent', default=True) +configitem('remotenames', 'precachedistance', default=True) +configitem('remotenames', 'pushanonheads', default=False) +configitem('remotenames', 'pushrev', default=None) +configitem('remotenames', 'resolvenodes', default=True) +configitem('remotenames', 'selectivepull', default=False) +configitem('remotenames', 'selectivepulldefault', default=[]) +configitem('remotenames', 'suppressbranches', default=False) +configitem('remotenames', 'syncbookmarks', default=False) +configitem('remotenames', 'tracking', default=True) +configitem('remotenames', 'transitionbookmarks', default=[]) +configitem('remotenames', 'transitionmessage', default=None) +configitem('remotenames', 'upstream', default=[]) + # namespace to use when recording an hg journal entry journalremotebookmarktype = 'remotebookmark' # name of the file that is used to mark that transition to selectivepull has # happened _selectivepullenabledfile = 'selectivepullenabled' +def uisetup(ui): +if registrar is None: +if util.safehasattr(uimod.ui, '_config'): +extensions.wrapfunction(uimod.ui, '_config', configdefault) +else: +extensions.wrapfunction(uimod.ui, 'config', configdefault) + def exbookcalcupdate(orig, ui, repo, checkout): '''Return a tuple (targetrev, movemarkfrom) indicating the rev to check out and where to move the active bookmark from, if needed.''' @@ -85,7 +139,7 @@ def expushop(orig, pushop, repo, remote, setattr(pushop, flag, kwargs.pop(flag, None)) def _isselectivepull(ui): -return ui.configbool('remotenames', 'selectivepull', False) +return ui.configbool('remotenames', 'selectivepull') def _getselectivepulldefaultbookmarks(ui): default_books = ui.configlist('remotenames', 'selectivepulldefault') @@ -186,7 +240,7 @@ def exfindcommonheads(orig, ui, local, r # the current remotenames are representative of what's on the server. In the # worst case the data might be slightly out of sync and the server sends us # more data than necessary, but this should be rare. -if not ui.configbool('remotenames', 'fastheaddiscovery', True): +if not ui.configbool('remotenames', 'fastheaddiscovery'): return orig(ui, local, remote, **kwargs) cl = local.changelog @@ -280,7 +334,7 @@ def blockerhook(orig, repo, *args, **kwa return blockers def exupdatefromremote(orig, ui, repo, remotemarks, path, trfunc, explicit=()): -if ui.configbool('remotenames', 'syncbookmarks',
[PATCH remotenames V2] configs: update configs to use registrar
# HG changeset patch # User Durham Goode <dur...@fb.com> # Date 1508879721 25200 # Tue Oct 24 14:15:21 2017 -0700 # Node ID c8d9ee2123baadd3ca1b571b3e7acf86ca1e6783 # Parent 5fb97b6b3d46fcd4dc1b3cb6be137762eb9e60e7 configs: update configs to use registrar Upstream now gives devel warnings if configs aren't registered, or if they specify defaults at access time. This updates remotenames to register all it's configs. It tries to be backwards compatible and support older clients who don't have config registering as well, but I haven't tested it well (testing against 4.0 crashes for other incompatibility reasons). diff --git a/remotenames.py b/remotenames.py --- a/remotenames.py +++ b/remotenames.py @@ -32,12 +32,14 @@ from mercurial import localrepo from mercurial import lock as lockmod from mercurial import namespaces from mercurial import obsutil +from mercurial import registrar from mercurial import repair from mercurial import repoview from mercurial import revset from mercurial import scmutil from mercurial import setdiscovery from mercurial import templatekw +from mercurial import ui as uimod from mercurial import url from mercurial import util from mercurial import vfs as vfsmod @@ -51,12 +53,64 @@ try: except ImportError: smartset = revset +configtable = {} +try: +from mercurial import registrar +if not util.safehasattr(registrar, 'configitem'): +raise ImportError() +# Supported in 4.3+ +configitem = registrar.configitem(configtable) +except ImportError: +# Support older releases that didn't register config defaults +registrar = None +def configitem(section, name, default=None): +configtable[(section, name)] = default + +def configdefault(orig, self, section, name, default=None, + untrusted=False): +if default is None: +default = configtable.get((section, name)) +return orig(self, section, name, default=default, untrusted=untrusted) + +configitem('remotenames', 'alias.default', default=False) +configitem('remotenames', 'allownonfastforward', default=False) +configitem('remotenames', 'bookmarks', default=True) +configitem('remotenames', 'branches', default=True) +configitem('remotenames', 'calculatedistance', default=True) +configitem('remotenames', 'disallowedbookmarks', default=[]) +configitem('remotenames', 'disallowedhint', default=None) +configitem('remotenames', 'disallowedto', default=None) +configitem('remotenames', 'fastheaddiscovery', default=True) +configitem('remotenames', 'forcecompat', default=False) +configitem('remotenames', 'forceto', default=False) +configitem('remotenames', 'hoist', default='default') +configitem('remotenames', 'precachecurrent', default=True) +configitem('remotenames', 'precachedistance', default=True) +configitem('remotenames', 'pushanonheads', default=False) +configitem('remotenames', 'pushrev', default=None) +configitem('remotenames', 'resolvenodes', default=True) +configitem('remotenames', 'selectivepull', default=False) +configitem('remotenames', 'selectivepulldefault', default=[]) +configitem('remotenames', 'suppressbranches', default=False) +configitem('remotenames', 'syncbookmarks', default=False) +configitem('remotenames', 'tracking', default=True) +configitem('remotenames', 'transitionbookmarks', default=[]) +configitem('remotenames', 'transitionmessage', default=None) +configitem('remotenames', 'upstream', default=[]) + # namespace to use when recording an hg journal entry journalremotebookmarktype = 'remotebookmark' # name of the file that is used to mark that transition to selectivepull has # happened _selectivepullenabledfile = 'selectivepullenabled' +def uisetup(ui): +if registrar is None: +if util.safehasattr(uimod.ui, '_config'): +extensions.wrapfunction(uimod.ui, '_config', configdefault) +else: +extensions.wrapfunction(uimod.ui, 'config', configdefault) + def exbookcalcupdate(orig, ui, repo, checkout): '''Return a tuple (targetrev, movemarkfrom) indicating the rev to check out and where to move the active bookmark from, if needed.''' @@ -85,7 +139,7 @@ def expushop(orig, pushop, repo, remote, setattr(pushop, flag, kwargs.pop(flag, None)) def _isselectivepull(ui): -return ui.configbool('remotenames', 'selectivepull', False) +return ui.configbool('remotenames', 'selectivepull') def _getselectivepulldefaultbookmarks(ui): default_books = ui.configlist('remotenames', 'selectivepulldefault') @@ -186,7 +240,7 @@ def exfindcommonheads(orig, ui, local, r # the current remotenames are representative of what's on the server. In the # worst case the data might be slightly out of sync and the server sends us # more data than necessary, but this should be rare. -if not ui.configbool('remotenames', 'fastheaddiscovery', True): +if not ui.configbool('remotenames', 'fastheaddiscovery'): return orig(ui, local,
[PATCH remotenames] configs: update configs to use registrar
# HG changeset patch # User Durham Goode <dur...@fb.com> # Date 1508879615 25200 # Tue Oct 24 14:13:35 2017 -0700 # Node ID 9ead5c33dd22a9a66cea009bcf76ffd801d5677e # Parent 5fb97b6b3d46fcd4dc1b3cb6be137762eb9e60e7 configs: update configs to use registrar diff --git a/remotenames.py b/remotenames.py --- a/remotenames.py +++ b/remotenames.py @@ -32,12 +32,14 @@ from mercurial import localrepo from mercurial import lock as lockmod from mercurial import namespaces from mercurial import obsutil +from mercurial import registrar from mercurial import repair from mercurial import repoview from mercurial import revset from mercurial import scmutil from mercurial import setdiscovery from mercurial import templatekw +from mercurial import ui as uimod from mercurial import url from mercurial import util from mercurial import vfs as vfsmod @@ -51,12 +53,64 @@ try: except ImportError: smartset = revset +configtable = {} +try: +from mercurial import registrar +if not util.safehasattr(registrar, 'configitem'): +raise ImportError() +# Supported in 4.3+ +configitem = registrar.configitem(configtable) +except ImportError: +# Support older releases that didn't register config defaults +registrar = None +def configitem(section, name, default=None): +configtable[(section, name)] = default + +def configdefault(orig, self, section, name, default=None, + untrusted=False): +if default is None: +default = configtable.get((section, name)) +return orig(self, section, name, default=default, untrusted=untrusted) + +configitem('remotenames', 'alias.default', default=False) +configitem('remotenames', 'allownonfastforward', default=False) +configitem('remotenames', 'bookmarks', default=True) +configitem('remotenames', 'branches', default=True) +configitem('remotenames', 'calculatedistance', default=True) +configitem('remotenames', 'disallowedbookmarks', default=[]) +configitem('remotenames', 'disallowedhint', default=None) +configitem('remotenames', 'disallowedto', default=None) +configitem('remotenames', 'fastheaddiscovery', default=True) +configitem('remotenames', 'forcecompat', default=False) +configitem('remotenames', 'forceto', default=False) +configitem('remotenames', 'hoist', default='default') +configitem('remotenames', 'precachecurrent', default=True) +configitem('remotenames', 'precachedistance', default=True) +configitem('remotenames', 'pushanonheads', default=False) +configitem('remotenames', 'pushrev', default=None) +configitem('remotenames', 'resolvenodes', default=True) +configitem('remotenames', 'selectivepull', default=False) +configitem('remotenames', 'selectivepulldefault', default=[]) +configitem('remotenames', 'suppressbranches', default=False) +configitem('remotenames', 'syncbookmarks', default=False) +configitem('remotenames', 'tracking', default=True) +configitem('remotenames', 'transitionbookmarks', default=[]) +configitem('remotenames', 'transitionmessage', default=None) +configitem('remotenames', 'upstream', default=[]) + # namespace to use when recording an hg journal entry journalremotebookmarktype = 'remotebookmark' # name of the file that is used to mark that transition to selectivepull has # happened _selectivepullenabledfile = 'selectivepullenabled' +def uisetup(ui): +if registrar is None: +if util.safehasattr(uimod.ui, '_config'): +extensions.wrapfunction(uimod.ui, '_config', configdefault) +else: +extensions.wrapfunction(uimod.ui, 'config', configdefault) + def exbookcalcupdate(orig, ui, repo, checkout): '''Return a tuple (targetrev, movemarkfrom) indicating the rev to check out and where to move the active bookmark from, if needed.''' @@ -85,7 +139,7 @@ def expushop(orig, pushop, repo, remote, setattr(pushop, flag, kwargs.pop(flag, None)) def _isselectivepull(ui): -return ui.configbool('remotenames', 'selectivepull', False) +return ui.configbool('remotenames', 'selectivepull') def _getselectivepulldefaultbookmarks(ui): default_books = ui.configlist('remotenames', 'selectivepulldefault') @@ -186,7 +240,7 @@ def exfindcommonheads(orig, ui, local, r # the current remotenames are representative of what's on the server. In the # worst case the data might be slightly out of sync and the server sends us # more data than necessary, but this should be rare. -if not ui.configbool('remotenames', 'fastheaddiscovery', True): +if not ui.configbool('remotenames', 'fastheaddiscovery'): return orig(ui, local, remote, **kwargs) cl = local.changelog @@ -280,7 +334,7 @@ def blockerhook(orig, repo, *args, **kwa return blockers def exupdatefromremote(orig, ui, repo, remotemarks, path, trfunc, explicit=()): -if ui.configbool('remotenames', 'syncbookmarks', False): +if ui.configbool('remotenames', 'syncbookmarks'): return orig(ui, repo, remot
Deploying registrar configs in a BC safe way
I've been trying to update our extensions to support the new registrar class, and one thing I've noticed is that it's not really possible to have code that works with old versions of mercurial and new versions of mercurial at the same time, without having devel warnings. I can easily stub out the registration logic if the registrar module doesn't exist, but I can't leave the default values on the actual config accessor lines, which old mercurial versions would need. For now I'm just updating our extensions to only work with new mercurial, but if someone tries to use them with an older version it will cause subtle failures as the default configs will be different. I tried to think of some way to fix this, like by leaving the defaults on the config access line, but ignoring it somehow, but that sounds funky. Thoughts on how to fix this? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D983: dirstate: move the _dirfoldmap to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe8a89ed7ce96: dirstate: move the _dirfoldmap to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D983?vs=2513=2713 REVISION DETAIL https://phab.mercurial-scm.org/D983 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -132,14 +132,6 @@ self._read() return self._map -@propertycache -def _dirfoldmap(self): -f = {} -normcase = util.normcase -for name in self._map.dirs: -f[normcase(name)] = name -return f - @property def _sparsematcher(self): """The matcher for the sparse checkout. @@ -372,8 +364,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_dirfoldmap", "_branch", - "_ignore"): +for a in ("_map", "_branch", "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -568,15 +559,15 @@ normed = util.normcase(path) folded = self._map.filefoldmap.get(normed, None) if folded is None: -folded = self._dirfoldmap.get(normed, None) +folded = self._map.dirfoldmap.get(normed, None) if folded is None: if isknown: folded = path else: # store discovered result in dirfoldmap so that future # normalizefile calls don't start matching directories folded = self._discoverpath(path, normed, ignoremissing, exists, -self._dirfoldmap) +self._map.dirfoldmap) return folded def normalize(self, path, isknown=False, ignoremissing=False): @@ -875,7 +866,7 @@ if len(paths) > 1: for path in paths: folded = self._discoverpath(path, norm, True, None, -self._dirfoldmap) +self._map.dirfoldmap) if path != folded: results[path] = None @@ -1396,3 +1387,10 @@ self.read() return self.identity +@propertycache +def dirfoldmap(self): +f = {} +normcase = util.normcase +for name in self.dirs: +f[normcase(name)] = name +return f diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -560,8 +560,8 @@ dirstate = repo.dirstate 'a' in dirstate def d(): -dirstate._dirfoldmap.get('a') -del dirstate._dirfoldmap +dirstate._map.dirfoldmap.get('a') +del dirstate._map.dirfoldmap del dirstate._map.dirs timer(d) fm.end() To: durham, #hg-reviewers, quark, durin42 Cc: quark, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D981: dirstate: remove _filefoldmap property cache
This revision was automatically updated to reflect the committed changes. Closed by commit rHGbfddc3d678ae: dirstate: remove _filefoldmap property cache (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D981?vs=2511=2710 REVISION DETAIL https://phab.mercurial-scm.org/D981 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -133,10 +133,6 @@ return self._map @propertycache -def _filefoldmap(self): -return self._map.filefoldmap() - -@propertycache def _dirfoldmap(self): f = {} normcase = util.normcase @@ -380,7 +376,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_filefoldmap", "_dirfoldmap", "_branch", +for a in ("_map", "_dirfoldmap", "_branch", "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) @@ -412,10 +408,10 @@ if self[f] not in "?r" and "_dirs" in self.__dict__: self._dirs.delpath(f) -if "_filefoldmap" in self.__dict__: +if "filefoldmap" in self._map.__dict__: normed = util.normcase(f) -if normed in self._filefoldmap: -del self._filefoldmap[normed] +if normed in self._map.filefoldmap: +del self._map.filefoldmap[normed] self._updatedfiles.add(f) @@ -563,18 +559,18 @@ def _normalizefile(self, path, isknown, ignoremissing=False, exists=None): normed = util.normcase(path) -folded = self._filefoldmap.get(normed, None) +folded = self._map.filefoldmap.get(normed, None) if folded is None: if isknown: folded = path else: folded = self._discoverpath(path, normed, ignoremissing, exists, -self._filefoldmap) +self._map.filefoldmap) return folded def _normalize(self, path, isknown, ignoremissing=False, exists=None): normed = util.normcase(path) -folded = self._filefoldmap.get(normed, None) +folded = self._map.filefoldmap.get(normed, None) if folded is None: folded = self._dirfoldmap.get(normed, None) if folded is None: @@ -1270,6 +1266,7 @@ otherparent.add(fname) return nonnorm, otherparent +@propertycache def filefoldmap(self): """Returns a dictionary mapping normalized case paths to their non-normalized versions. diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -549,8 +549,8 @@ dirstate = repo.dirstate 'a' in dirstate def d(): -dirstate._filefoldmap.get('a') -del dirstate._filefoldmap +dirstate._map.filefoldmap.get('a') +del dirstate._map.filefoldmap timer(d) fm.end() To: durham, #hg-reviewers, quark, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D982: dirstate: remove _dirs property cache
This revision was automatically updated to reflect the committed changes. Closed by commit rHG014bd2a555c8: dirstate: remove _dirs property cache (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D982?vs=2512=2711 REVISION DETAIL https://phab.mercurial-scm.org/D982 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -136,7 +136,7 @@ def _dirfoldmap(self): f = {} normcase = util.normcase -for name in self._dirs: +for name in self._map.dirs: f[normcase(name)] = name return f @@ -166,12 +166,8 @@ def _pl(self): return self._map.parents() -@propertycache -def _dirs(self): -return self._map.dirs() - def dirs(self): -return self._dirs +return self._map.dirs @rootcache('.hgignore') def _ignore(self): @@ -377,7 +373,7 @@ check whether the dirstate has changed before rereading it.''' for a in ("_map", "_dirfoldmap", "_branch", - "_dirs", "_ignore"): + "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -405,8 +401,8 @@ return self._map.copymap def _droppath(self, f): -if self[f] not in "?r" and "_dirs" in self.__dict__: -self._dirs.delpath(f) +if self[f] not in "?r" and "dirs" in self._map.__dict__: +self._map.dirs.delpath(f) if "filefoldmap" in self._map.__dict__: normed = util.normcase(f) @@ -419,18 +415,18 @@ oldstate = self[f] if state == 'a' or oldstate == 'r': scmutil.checkfilename(f) -if f in self._dirs: +if f in self._map.dirs: raise error.Abort(_('directory %r already in dirstate') % f) # shadows for d in util.finddirs(f): -if d in self._dirs: +if d in self._map.dirs: break entry = self._map.get(d) if entry is not None and entry[0] != 'r': raise error.Abort( _('file %r in dirstate clashes with %r') % (d, f)) -if oldstate in "?r" and "_dirs" in self.__dict__: -self._dirs.addpath(f) +if oldstate in "?r" and "dirs" in self._map.__dict__: +self._map.dirs.addpath(f) self._dirty = True self._updatedfiles.add(f) self._map[f] = dirstatetuple(state, mode, size, mtime) @@ -607,8 +603,6 @@ def clear(self): self._map = dirstatemap(self._ui, self._opener, self._root) -if "_dirs" in self.__dict__: -delattr(self, "_dirs") self._map.setparents(nullid, nullid) self._lastnormaltime = 0 self._updatedfiles.clear() @@ -1287,6 +1281,7 @@ f['.'] = '.' # prevents useless util.fspath() invocation return f +@propertycache def dirs(self): """Returns a set-like object containing all the directories in the current dirstate. diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -519,7 +519,7 @@ 'a' in dirstate def d(): dirstate.dirs() -del dirstate._dirs +del dirstate._map.dirs timer(d) fm.end() @@ -538,8 +538,8 @@ timer, fm = gettimer(ui, opts) "a" in repo.dirstate def d(): -"a" in repo.dirstate._dirs -del repo.dirstate._dirs +"a" in repo.dirstate._map.dirs +del repo.dirstate._map.dirs timer(d) fm.end() @@ -562,7 +562,7 @@ def d(): dirstate._dirfoldmap.get('a') del dirstate._dirfoldmap -del dirstate._dirs +del dirstate._map.dirs timer(d) fm.end() To: durham, #hg-reviewers, quark, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D979: dirstate: move nonnormal and otherparent sets to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG60927b19ed65: dirstate: move nonnormal and otherparent sets to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D979?vs=2509=2708 REVISION DETAIL https://phab.mercurial-scm.org/D979 AFFECTED FILES contrib/dirstatenonnormalcheck.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -138,18 +138,6 @@ return self._identity @propertycache -def _nonnormalset(self): -nonnorm, otherparents = self._map.nonnormalentries() -self._otherparentset = otherparents -return nonnorm - -@propertycache -def _otherparentset(self): -nonnorm, otherparents = self._map.nonnormalentries() -self._nonnormalset = nonnorm -return otherparents - -@propertycache def _filefoldmap(self): return self._map.filefoldmap() @@ -349,7 +337,8 @@ self._map.setparents(p1, p2) copies = {} if oldp2 != nullid and p2 == nullid: -candidatefiles = self._nonnormalset.union(self._otherparentset) +candidatefiles = self._map.nonnormalset.union( +self._map.otherparentset) for f in candidatefiles: s = self._map.get(f) if s is None: @@ -401,8 +390,7 @@ for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", - "_dirs", "_ignore", "_nonnormalset", - "_otherparentset"): + "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -460,19 +448,19 @@ self._updatedfiles.add(f) self._map[f] = dirstatetuple(state, mode, size, mtime) if state != 'n' or mtime == -1: -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) if size == -2: -self._otherparentset.add(f) +self._map.otherparentset.add(f) def normal(self, f): '''Mark a file normal and clean.''' s = os.lstat(self._join(f)) mtime = s.st_mtime self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) self._map.copymap.pop(f, None) -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) if mtime > self._lastnormaltime: # Remember the most recent modification timeslot for status(), # to make sure we won't miss future size-preserving file content @@ -500,8 +488,8 @@ return self._addpath(f, 'n', 0, -1, -1) self._map.copymap.pop(f, None) -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) def otherparent(self, f): '''Mark as coming from the other parent, always dirty.''' @@ -534,9 +522,9 @@ size = -1 elif entry[0] == 'n' and entry[2] == -2: # other parent size = -2 -self._otherparentset.add(f) +self._map.otherparentset.add(f) self._map[f] = dirstatetuple('r', 0, size, 0) -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) if size == 0: self._map.copymap.pop(f, None) @@ -552,8 +540,8 @@ self._dirty = True self._droppath(f) del self._map[f] -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) self._map.copymap.pop(f, None) def _discoverpath(self, path, normed, ignoremissing, exists, storemap): @@ -632,8 +620,6 @@ def clear(self): self._map = dirstatemap(self._ui, self._opener, self._root) -self._nonnormalset = set() -self._otherparentset = set() if "_dirs" in self.__dict__: delattr(self, "_dirs") self._map.setparents(nullid, nullid) @@ -687,7 +673,7 @@ e = dmap.get(f) if e is not None and e[0] == 'n' and e[3] == now: dmap[f] = dirstatetuple(e[0], e[1], e[2], -1) -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) # emulate that all 'dirstate.normal' results are written out self._lastnormaltime = 0 @@ -740,7 +726,6 @@ break self._map.write(st, now) -self._nonnormalset, self._otherparentset =
D978: dirstate: move write into dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe2214632c3a2: dirstate: move write into dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D978?vs=2508=2707 REVISION DETAIL https://phab.mercurial-scm.org/D978 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -739,12 +739,10 @@ now = end # trust our estimate that the end is near now break -st.write(parsers.pack_dirstate(self._map._map, self._map.copymap, - self._pl, now)) +self._map.write(st, now) self._nonnormalset, self._otherparentset = self._map.nonnormalentries() -st.close() self._lastnormaltime = 0 -self._dirty = self._map._dirtyparents = False +self._dirty = False def _dirignore(self, f): if f == '.': @@ -1401,3 +1399,9 @@ p = parse_dirstate(self._map, self.copymap, st) if not self._dirtyparents: self.setparents(*p) + +def write(self, st, now): +st.write(parsers.pack_dirstate(self._map, self.copymap, + self.parents(), now)) +st.close() +self._dirtyparents = False To: durham, #hg-reviewers, quark, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D977: dirstate: move _read into dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe159f217230e: dirstate: move _read into dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D977?vs=2507=2706 REVISION DETAIL https://phab.mercurial-scm.org/D977 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -386,53 +386,11 @@ raise def _read(self): -self._map = dirstatemap(self._ui, self._opener, self._root) - # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) -try: -fp = self._map._opendirstatefile() -try: -st = fp.read() -finally: -fp.close() -except IOError as err: -if err.errno != errno.ENOENT: -raise -return -if not st: -return - -if util.safehasattr(parsers, 'dict_new_presized'): -# Make an estimate of the number of files in the dirstate based on -# its size. From a linear regression on a set of real-world repos, -# all over 10,000 files, the size of a dirstate entry is 85 -# bytes. The cost of resizing is significantly higher than the cost -# of filling in a larger presized dict, so subtract 20% from the -# size. -# -# This heuristic is imperfect in many ways, so in a future dirstate -# format update it makes sense to just record the number of entries -# on write. -self._map._map = parsers.dict_new_presized(len(st) / 71) - -# Python's garbage collector triggers a GC each time a certain number -# of container objects (the number being defined by -# gc.get_threshold()) are allocated. parse_dirstate creates a tuple -# for each file in the dirstate. The C version then immediately marks -# them as not to be tracked by the collector. However, this has no -# effect on when GCs are triggered, only on what objects the GC looks -# into. This means that O(number of files) GCs are unavoidable. -# Depending on when in the process's lifetime the dirstate is parsed, -# this can get very expensive. As a workaround, disable GC while -# parsing the dirstate. -# -# (we cannot decorate the function directly since it is in a C module) -parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map._map, self._map.copymap, st) -if not self._map._dirtyparents: -self._map.setparents(*p) +self._map = dirstatemap(self._ui, self._opener, self._root) +self._map.read() def invalidate(self): '''Causes the next access to reread the dirstate. @@ -1399,3 +1357,47 @@ def setparents(self, p1, p2): self._parents = (p1, p2) self._dirtyparents = True + +def read(self): +try: +fp = self._opendirstatefile() +try: +st = fp.read() +finally: +fp.close() +except IOError as err: +if err.errno != errno.ENOENT: +raise +return +if not st: +return + +if util.safehasattr(parsers, 'dict_new_presized'): +# Make an estimate of the number of files in the dirstate based on +# its size. From a linear regression on a set of real-world repos, +# all over 10,000 files, the size of a dirstate entry is 85 +# bytes. The cost of resizing is significantly higher than the cost +# of filling in a larger presized dict, so subtract 20% from the +# size. +# +# This heuristic is imperfect in many ways, so in a future dirstate +# format update it makes sense to just record the number of entries +# on write. +self._map = parsers.dict_new_presized(len(st) / 71) + +# Python's garbage collector triggers a GC each time a certain number +# of container objects (the number being defined by +# gc.get_threshold()) are allocated. parse_dirstate creates a tuple +# for each file in the dirstate. The C version then immediately marks +# them as not to be tracked by the collector. However, this has no +# effect on when GCs are triggered, only on what objects the GC looks +# into. This means that O(number of files) GCs are unavoidable. +# Depending on when in the process's lifetime the dirstate is parsed, +# this can get very expensive. As a workaround, disable GC while +# parsing the
D980: dirstate: move identity to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHGc6ef9a2498a5: dirstate: move identity to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D980?vs=2510=2709 REVISION DETAIL https://phab.mercurial-scm.org/D980 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -133,11 +133,6 @@ return self._map @propertycache -def _identity(self): -self._read() -return self._identity - -@propertycache def _filefoldmap(self): return self._map.filefoldmap() @@ -375,9 +370,6 @@ raise def _read(self): -# ignore HG_PENDING because identity is used only for writing -self._identity = util.filestat.frompath( -self._opener.join(self._filename)) self._map = dirstatemap(self._ui, self._opener, self._root) self._map.read() @@ -388,8 +380,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_identity", - "_filefoldmap", "_dirfoldmap", "_branch", +for a in ("_map", "_filefoldmap", "_dirfoldmap", "_branch", "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) @@ -652,7 +643,7 @@ If identity of previous dirstate is equal to this, writing changes based on the former dirstate out can keep consistency. ''' -return self._identity +return self._map.identity def write(self, tr): if not self._dirty: @@ -1342,6 +1333,10 @@ self._dirtyparents = True def read(self): +# ignore HG_PENDING because identity is used only for writing +self.identity = util.filestat.frompath( +self._opener.join(self._filename)) + try: fp = self._opendirstatefile() try: @@ -1404,3 +1399,8 @@ self.nonnormalset = nonnorm return otherparents +@propertycache +def identity(self): +self.read() +return self.identity + To: durham, #hg-reviewers, quark, durin42 Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D960: bundle2: immediate exit for ctrl+c (issue5692)
durham added inline comments. INLINE COMMENTS > yuja wrote in bundle2.py:380 > SignalInterrupt is a subclass of KeyboardInterrupt, so there might > be deeper problem. > > FWIW, I think it's probably a good idea to replace this blacklist > with `isinstance(exc, Exception)`. Before my refactor there used to be two levels of error handling, 1) inside _parthandler which tried to seek to the end if it wasn't systemexit or keyboardinterrup, and 2) inside processbundle (which calls _parthandler) which only caught type Exception. It seems like the correct unification of these would be to just keep the `isinstance(exc, Exception)` check above, and delete this sub-check entirely. I'll send an update. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D960 To: durham, #hg-reviewers, quark Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D945: fsmonitor: update to match new dirstate refactor
This revision was automatically updated to reflect the committed changes. Closed by commit rHG7259f0ddfc0f: fsmonitor: update to match new dirstate refactor (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D945?vs=2536=2583 REVISION DETAIL https://phab.mercurial-scm.org/D945 AFFECTED FILES hgext/fsmonitor/__init__.py CHANGE DETAILS diff --git a/hgext/fsmonitor/__init__.py b/hgext/fsmonitor/__init__.py --- a/hgext/fsmonitor/__init__.py +++ b/hgext/fsmonitor/__init__.py @@ -251,7 +251,7 @@ matchfn = match.matchfn matchalways = match.always() -dmap = self._map +dmap = self._map._map nonnormalset = getattr(self, '_nonnormalset', None) copymap = self._map.copymap To: durham, #hg-reviewers, quark, yuja, ryanmce Cc: ryanmce, yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D981: dirstate: remove _filefoldmap property cache
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that the filefoldmap is source of truthed on the dirstatemap, let's get rid of the property cache on the dirstate. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D981 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -133,10 +133,6 @@ return self._map @propertycache -def _filefoldmap(self): -return self._map.filefoldmap() - -@propertycache def _dirfoldmap(self): f = {} normcase = util.normcase @@ -380,7 +376,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_filefoldmap", "_dirfoldmap", "_branch", +for a in ("_map", "_dirfoldmap", "_branch", "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) @@ -412,10 +408,10 @@ if self[f] not in "?r" and "_dirs" in self.__dict__: self._dirs.delpath(f) -if "_filefoldmap" in self.__dict__: +if "filefoldmap" in self._map.__dict__: normed = util.normcase(f) -if normed in self._filefoldmap: -del self._filefoldmap[normed] +if normed in self._map.filefoldmap: +del self._map.filefoldmap[normed] self._updatedfiles.add(f) @@ -563,18 +559,18 @@ def _normalizefile(self, path, isknown, ignoremissing=False, exists=None): normed = util.normcase(path) -folded = self._filefoldmap.get(normed, None) +folded = self._map.filefoldmap.get(normed, None) if folded is None: if isknown: folded = path else: folded = self._discoverpath(path, normed, ignoremissing, exists, -self._filefoldmap) +self._map.filefoldmap) return folded def _normalize(self, path, isknown, ignoremissing=False, exists=None): normed = util.normcase(path) -folded = self._filefoldmap.get(normed, None) +folded = self._map.filefoldmap.get(normed, None) if folded is None: folded = self._dirfoldmap.get(normed, None) if folded is None: @@ -1270,6 +1266,7 @@ otherparent.add(fname) return nonnorm, otherparent +@propertycache def filefoldmap(self): """Returns a dictionary mapping normalized case paths to their non-normalized versions. diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -539,8 +539,8 @@ dirstate = repo.dirstate 'a' in dirstate def d(): -dirstate._filefoldmap.get('a') -del dirstate._filefoldmap +dirstate._map.filefoldmap.get('a') +del dirstate._map.filefoldmap timer(d) fm.end() To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D982: dirstate: remove _dirs property cache
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that dirs is source of truthed on the dirstatemap, let's get rid of the _dirs propertycache on the dirstate. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D982 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -136,7 +136,7 @@ def _dirfoldmap(self): f = {} normcase = util.normcase -for name in self._dirs: +for name in self._map.dirs: f[normcase(name)] = name return f @@ -166,12 +166,8 @@ def _pl(self): return self._map.parents() -@propertycache -def _dirs(self): -return self._map.dirs() - def dirs(self): -return self._dirs +return self._map.dirs @rootcache('.hgignore') def _ignore(self): @@ -377,7 +373,7 @@ check whether the dirstate has changed before rereading it.''' for a in ("_map", "_dirfoldmap", "_branch", - "_dirs", "_ignore"): + "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -405,8 +401,8 @@ return self._map.copymap def _droppath(self, f): -if self[f] not in "?r" and "_dirs" in self.__dict__: -self._dirs.delpath(f) +if self[f] not in "?r" and "dirs" in self._map.__dict__: +self._map.dirs.delpath(f) if "filefoldmap" in self._map.__dict__: normed = util.normcase(f) @@ -419,18 +415,18 @@ oldstate = self[f] if state == 'a' or oldstate == 'r': scmutil.checkfilename(f) -if f in self._dirs: +if f in self._map.dirs: raise error.Abort(_('directory %r already in dirstate') % f) # shadows for d in util.finddirs(f): -if d in self._dirs: +if d in self._map.dirs: break entry = self._map.get(d) if entry is not None and entry[0] != 'r': raise error.Abort( _('file %r in dirstate clashes with %r') % (d, f)) -if oldstate in "?r" and "_dirs" in self.__dict__: -self._dirs.addpath(f) +if oldstate in "?r" and "dirs" in self._map.__dict__: +self._map.dirs.addpath(f) self._dirty = True self._updatedfiles.add(f) self._map[f] = dirstatetuple(state, mode, size, mtime) @@ -607,8 +603,6 @@ def clear(self): self._map = dirstatemap(self._ui, self._opener, self._root) -if "_dirs" in self.__dict__: -delattr(self, "_dirs") self._map.setparents(nullid, nullid) self._lastnormaltime = 0 self._updatedfiles.clear() @@ -1287,6 +1281,7 @@ f['.'] = '.' # prevents useless util.fspath() invocation return f +@propertycache def dirs(self): """Returns a set-like object containing all the directories in the current dirstate. diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -509,7 +509,7 @@ 'a' in dirstate def d(): dirstate.dirs() -del dirstate._dirs +del dirstate._map.dirs timer(d) fm.end() @@ -528,8 +528,8 @@ timer, fm = gettimer(ui, opts) "a" in repo.dirstate def d(): -"a" in repo.dirstate._dirs -del repo.dirstate._dirs +"a" in repo.dirstate._map.dirs +del repo.dirstate._map.dirs timer(d) fm.end() @@ -552,7 +552,7 @@ def d(): dirstate._dirfoldmap.get('a') del dirstate._dirfoldmap -del dirstate._dirs +del dirstate._map.dirs timer(d) fm.end() To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D983: dirstate: move the _dirfoldmap to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that dirstatemap is the source of truth for the list of directories, let's move _dirfoldmap on to it. This pattern of moving cached variables onto the dirstate map makes it easier to invalidate them, as seen by how the cache invalidation functions are slowly shrinking to just be recreating the dirstatemap instance. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D983 AFFECTED FILES contrib/perf.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -132,14 +132,6 @@ self._read() return self._map -@propertycache -def _dirfoldmap(self): -f = {} -normcase = util.normcase -for name in self._map.dirs: -f[normcase(name)] = name -return f - @property def _sparsematcher(self): """The matcher for the sparse checkout. @@ -372,8 +364,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_dirfoldmap", "_branch", - "_ignore"): +for a in ("_map", "_branch", "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -568,15 +559,15 @@ normed = util.normcase(path) folded = self._map.filefoldmap.get(normed, None) if folded is None: -folded = self._dirfoldmap.get(normed, None) +folded = self._map.dirfoldmap.get(normed, None) if folded is None: if isknown: folded = path else: # store discovered result in dirfoldmap so that future # normalizefile calls don't start matching directories folded = self._discoverpath(path, normed, ignoremissing, exists, -self._dirfoldmap) +self._map.dirfoldmap) return folded def normalize(self, path, isknown=False, ignoremissing=False): @@ -875,7 +866,7 @@ if len(paths) > 1: for path in paths: folded = self._discoverpath(path, norm, True, None, -self._dirfoldmap) +self._map.dirfoldmap) if path != folded: results[path] = None @@ -1396,3 +1387,10 @@ self.read() return self.identity +@propertycache +def dirfoldmap(self): +f = {} +normcase = util.normcase +for name in self.dirs: +f[normcase(name)] = name +return f diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -550,8 +550,8 @@ dirstate = repo.dirstate 'a' in dirstate def d(): -dirstate._dirfoldmap.get('a') -del dirstate._dirfoldmap +dirstate._map.dirfoldmap.get('a') +del dirstate._map.dirfoldmap del dirstate._map.dirs timer(d) fm.end() To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D979: dirstate: move nonnormal and otherparent sets to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of separating dirstate business logic from storage, let's move the nonnormal and otherparent storage to the dirstatemap class. This will allow alternative dirstate storage to persist these sets instead of recomputing them. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D979 AFFECTED FILES contrib/dirstatenonnormalcheck.py mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -138,18 +138,6 @@ return self._identity @propertycache -def _nonnormalset(self): -nonnorm, otherparents = self._map.nonnormalentries() -self._otherparentset = otherparents -return nonnorm - -@propertycache -def _otherparentset(self): -nonnorm, otherparents = self._map.nonnormalentries() -self._nonnormalset = nonnorm -return otherparents - -@propertycache def _filefoldmap(self): return self._map.filefoldmap() @@ -349,7 +337,8 @@ self._map.setparents(p1, p2) copies = {} if oldp2 != nullid and p2 == nullid: -candidatefiles = self._nonnormalset.union(self._otherparentset) +candidatefiles = self._map.nonnormalset.union( +self._map.otherparentset) for f in candidatefiles: s = self._map.get(f) if s is None: @@ -401,8 +390,7 @@ for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", - "_dirs", "_ignore", "_nonnormalset", - "_otherparentset"): + "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) self._lastnormaltime = 0 @@ -460,19 +448,19 @@ self._updatedfiles.add(f) self._map[f] = dirstatetuple(state, mode, size, mtime) if state != 'n' or mtime == -1: -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) if size == -2: -self._otherparentset.add(f) +self._map.otherparentset.add(f) def normal(self, f): '''Mark a file normal and clean.''' s = os.lstat(self._join(f)) mtime = s.st_mtime self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) self._map.copymap.pop(f, None) -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) if mtime > self._lastnormaltime: # Remember the most recent modification timeslot for status(), # to make sure we won't miss future size-preserving file content @@ -500,8 +488,8 @@ return self._addpath(f, 'n', 0, -1, -1) self._map.copymap.pop(f, None) -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) def otherparent(self, f): '''Mark as coming from the other parent, always dirty.''' @@ -534,9 +522,9 @@ size = -1 elif entry[0] == 'n' and entry[2] == -2: # other parent size = -2 -self._otherparentset.add(f) +self._map.otherparentset.add(f) self._map[f] = dirstatetuple('r', 0, size, 0) -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) if size == 0: self._map.copymap.pop(f, None) @@ -552,8 +540,8 @@ self._dirty = True self._droppath(f) del self._map[f] -if f in self._nonnormalset: -self._nonnormalset.remove(f) +if f in self._map.nonnormalset: +self._map.nonnormalset.remove(f) self._map.copymap.pop(f, None) def _discoverpath(self, path, normed, ignoremissing, exists, storemap): @@ -632,8 +620,6 @@ def clear(self): self._map = dirstatemap(self._ui, self._opener, self._root) -self._nonnormalset = set() -self._otherparentset = set() if "_dirs" in self.__dict__: delattr(self, "_dirs") self._map.setparents(nullid, nullid) @@ -687,7 +673,7 @@ e = dmap.get(f) if e is not None and e[0] == 'n' and e[3] == now: dmap[f] = dirstatetuple(e[0], e[1], e[2], -1) -self._nonnormalset.add(f) +self._map.nonnormalset.add(f) # emulate that all 'dirstate.normal' results are written out self._lastnormaltime = 0 @@ -740,7 +726,6 @@ break
D980: dirstate: move identity to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Moving the identity function to the dirstatemap class will allow alternative dirstate implementations to replace the implementation. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D980 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -133,11 +133,6 @@ return self._map @propertycache -def _identity(self): -self._read() -return self._identity - -@propertycache def _filefoldmap(self): return self._map.filefoldmap() @@ -375,9 +370,6 @@ raise def _read(self): -# ignore HG_PENDING because identity is used only for writing -self._identity = util.filestat.frompath( -self._opener.join(self._filename)) self._map = dirstatemap(self._ui, self._opener, self._root) self._map.read() @@ -388,8 +380,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_identity", - "_filefoldmap", "_dirfoldmap", "_branch", +for a in ("_map", "_filefoldmap", "_dirfoldmap", "_branch", "_dirs", "_ignore"): if a in self.__dict__: delattr(self, a) @@ -652,7 +643,7 @@ If identity of previous dirstate is equal to this, writing changes based on the former dirstate out can keep consistency. ''' -return self._identity +return self._map.identity def write(self, tr): if not self._dirty: @@ -1342,6 +1333,10 @@ self._dirtyparents = True def read(self): +# ignore HG_PENDING because identity is used only for writing +self.identity = util.filestat.frompath( +self._opener.join(self._filename)) + try: fp = self._opendirstatefile() try: @@ -1404,3 +1399,8 @@ self.nonnormalset = nonnorm return otherparents +@propertycache +def identity(self): +self.read() +return self.identity + To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D978: dirstate: move write into dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of separating the dirstate business logic from the dirstate storage logic, let's move the serialization code down into dirstatemap. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D978 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -739,12 +739,10 @@ now = end # trust our estimate that the end is near now break -st.write(parsers.pack_dirstate(self._map._map, self._map.copymap, - self._pl, now)) +self._map.write(st, now) self._nonnormalset, self._otherparentset = self._map.nonnormalentries() -st.close() self._lastnormaltime = 0 -self._dirty = self._map._dirtyparents = False +self._dirty = False def _dirignore(self, f): if f == '.': @@ -1401,3 +1399,9 @@ p = parse_dirstate(self._map, self.copymap, st) if not self._dirtyparents: self.setparents(*p) + +def write(self, st, now): +st.write(parsers.pack_dirstate(self._map, self.copymap, + self.parents(), now)) +st.close() +self._dirtyparents = False To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D960: bundle2: immediate exit for ctrl+c (issue5692)
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY https://phab.mercurial-scm.org/rHG21c2df59a1dad534bfac45acc0bbfb6cb2afe9b4 regressed bundle2 by catching all exceptions and trying to handle them. The old behavior was to allow KeyboardInterrupts to throw and not have graceful cleanup, which allowed it to exit immediately. Let's go back to that behavior. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D960 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -370,7 +370,10 @@ if not self.iterator: return -if exc: +# Only gracefully abort in a normal exception situation. User aborts +# like Ctrl+C throw a KeyboardInterrupt which is not a base Exception, +# and should not gracefully cleanup. +if isinstance(exc, Exception): # If exiting or interrupted, do not attempt to seek the stream in # the finally block below. This makes abort faster. if (self.current and To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH phase] exchange: fix test for remote support of binary phases
On 9/30/17 6:26 AM, Yuya Nishihara wrote: On Sat, 30 Sep 2017 16:01:36 +0530, Pulkit Goyal wrote: I don't see the traceback after applying this patch. If possible, this patch should be folded with the previous series. On Sat, Sep 30, 2017 at 3:05 PM, Boris Feldwrote: # HG changeset patch # User Boris Feld # Date 1506762569 -3600 # Sat Sep 30 10:09:29 2017 +0100 # Node ID 4d26dbcdfdb38fd3fc4efa157b64819f5281c8e0 # Parent ec769bba34d377503f42308dea5ddcb8ce4c0ade # EXP-Topic fix-phases exchange: fix test for remote support of binary phases Queued, thanks. Should there be a test to cover this case? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D945: fsmonitor: update to match new dirstate refactor
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The dirstate was refactored so dirstate._map is now at dirstate._map._map. Same for _copymap, is not _map.copymap. It seems none of the mercurial tests cover this stuff, but it was caught by our Facebook extension tests. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D945 AFFECTED FILES hgext/fsmonitor/__init__.py CHANGE DETAILS diff --git a/hgext/fsmonitor/__init__.py b/hgext/fsmonitor/__init__.py --- a/hgext/fsmonitor/__init__.py +++ b/hgext/fsmonitor/__init__.py @@ -228,10 +228,10 @@ matchfn = match.matchfn matchalways = match.always() -dmap = self._map +dmap = self._map._map nonnormalset = getattr(self, '_nonnormalset', None) -copymap = self._copymap +copymap = self._map.copymap getkind = stat.S_IFMT dirkind = stat.S_IFDIR regkind = stat.S_IFREG To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D759: dirstate: move parents source of truth to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG5fabba9b3d9c: dirstate: move parents source of truth to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D759?vs=2083=2162 REVISION DETAIL https://phab.mercurial-scm.org/D759 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -71,7 +71,6 @@ # UNC path pointing to root share (issue4557) self._rootdir = pathutil.normasprefix(root) self._dirty = False -self._dirtypl = False self._lastnormaltime = 0 self._ui = ui self._filecache = {} @@ -184,7 +183,7 @@ raise return "default" -@propertycache +@property def _pl(self): return self._map.parents() @@ -343,11 +342,11 @@ raise ValueError("cannot set dirstate parent without " "calling dirstate.beginparentchange") -self._dirty = self._dirtypl = True +self._dirty = True oldp2 = self._pl[1] if self._origpl is None: self._origpl = self._pl -self._pl = p1, p2 +self._map.setparents(p1, p2) copies = {} if oldp2 != nullid and p2 == nullid: candidatefiles = self._nonnormalset.union(self._otherparentset) @@ -432,8 +431,8 @@ # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) p = parse_dirstate(self._map._map, self._map.copymap, st) -if not self._dirtypl: -self._pl = p +if not self._map._dirtyparents: +self._map.setparents(*p) def invalidate(self): '''Causes the next access to reread the dirstate. @@ -444,7 +443,7 @@ for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", - "_pl", "_dirs", "_ignore", "_nonnormalset", + "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): if a in self.__dict__: delattr(self, a) @@ -679,7 +678,7 @@ self._otherparentset = set() if "_dirs" in self.__dict__: delattr(self, "_dirs") -self._pl = [nullid, nullid] +self._map.setparents(nullid, nullid) self._lastnormaltime = 0 self._updatedfiles.clear() self._dirty = True @@ -694,7 +693,7 @@ if self._origpl is None: self._origpl = self._pl -self._pl = (parent, nullid) +self._map.setparents(parent, nullid) for f in changedfiles: if f in allfiles: self.normallookup(f) @@ -787,7 +786,7 @@ self._nonnormalset, self._otherparentset = self._map.nonnormalentries() st.close() self._lastnormaltime = 0 -self._dirty = self._dirtypl = False +self._dirty = self._map._dirtyparents = False def _dirignore(self, f): if f == '.': @@ -1292,6 +1291,8 @@ self._map = {} self.copymap = {} +self._parents = None +self._dirtyparents = False # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -1370,16 +1371,28 @@ return fp def parents(self): -try: -fp = self._opendirstatefile() -st = fp.read(40) -fp.close() +if not self._parents: +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +except IOError as err: +if err.errno != errno.ENOENT: +raise +# File doesn't exist, so the current state is empty +st = '' + l = len(st) if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +self._parents = st[:20], st[20:40] +elif l == 0: +self._parents = [nullid, nullid] +else: +raise error.Abort(_('working directory state appears ' +'damaged!')) + +return self._parents + +def setparents(self, p1, p2): +self._parents = (p1, p2) +self._dirtyparents = True To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D754: dirstate: move filefoldmap to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG19a5ad535cb1: dirstate: move filefoldmap to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D754?vs=2078=2157 REVISION DETAIL https://phab.mercurial-scm.org/D754 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -160,21 +160,7 @@ @propertycache def _filefoldmap(self): -try: -makefilefoldmap = parsers.make_file_foldmap -except AttributeError: -pass -else: -return makefilefoldmap(self._map._map, util.normcasespec, - util.normcasefallback) - -f = {} -normcase = util.normcase -for name, s in self._map.iteritems(): -if s[0] != 'r': -f[normcase(name)] = name -f['.'] = '.' # prevents useless util.fspath() invocation -return f +return self._map.filefoldmap() @propertycache def _dirfoldmap(self): @@ -1370,3 +1356,22 @@ otherparent.add(fname) return nonnorm, otherparent +def filefoldmap(self): +"""Returns a dictionary mapping normalized case paths to their +non-normalized versions. +""" +try: +makefilefoldmap = parsers.make_file_foldmap +except AttributeError: +pass +else: +return makefilefoldmap(self._map, util.normcasespec, + util.normcasefallback) + +f = {} +normcase = util.normcase +for name, s in self._map.iteritems(): +if s[0] != 'r': +f[normcase(name)] = name +f['.'] = '.' # prevents useless util.fspath() invocation +return f To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D758: dirstate: move parent reading to the dirstatemap class
This revision was automatically updated to reflect the committed changes. Closed by commit rHG949276a8cd76: dirstate: move parent reading to the dirstatemap class (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D758?vs=2082=2161 REVISION DETAIL https://phab.mercurial-scm.org/D758 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -186,19 +186,7 @@ @propertycache def _pl(self): -try: -fp = self._map._opendirstatefile() -st = fp.read(40) -fp.close() -l = len(st) -if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +return self._map.parents() @propertycache def _dirs(self): @@ -1381,3 +1369,17 @@ self._pendingmode = mode return fp +def parents(self): +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +l = len(st) +if l == 40: +return st[:20], st[20:40] +elif l > 0 and l < 40: +raise error.Abort(_('working directory state appears damaged!')) +except IOError as err: +if err.errno != errno.ENOENT: +raise +return [nullid, nullid] To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D752: dirstate: create new dirstatemap class
This revision was automatically updated to reflect the committed changes. Closed by commit rHG42b68c9c2742: dirstate: create new dirstatemap class (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D752?vs=1947=2155 REVISION DETAIL https://phab.mercurial-scm.org/D752 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -57,7 +57,7 @@ def nonnormalentries(dmap): '''Compute the nonnormal dirstate entries from the dmap''' try: -return parsers.nonnormalotherparententries(dmap) +return parsers.nonnormalotherparententries(dmap._map) except AttributeError: nonnorm = set() otherparent = set() @@ -179,7 +179,7 @@ except AttributeError: pass else: -return makefilefoldmap(self._map, util.normcasespec, +return makefilefoldmap(self._map._map, util.normcasespec, util.normcasefallback) f = {} @@ -238,7 +238,7 @@ @propertycache def _dirs(self): -return util.dirs(self._map, 'r') +return util.dirs(self._map._map, 'r') def dirs(self): return self._dirs @@ -444,7 +444,8 @@ return fp def _read(self): -self._map = {} +self._map = dirstatemap() + self._copymap = {} # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( @@ -473,7 +474,7 @@ # This heuristic is imperfect in many ways, so in a future dirstate # format update it makes sense to just record the number of entries # on write. -self._map = parsers.dict_new_presized(len(st) / 71) +self._map._map = parsers.dict_new_presized(len(st) / 71) # Python's garbage collector triggers a GC each time a certain number # of container objects (the number being defined by @@ -488,7 +489,7 @@ # # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map, self._copymap, st) +p = parse_dirstate(self._map._map, self._copymap, st) if not self._dirtypl: self._pl = p @@ -731,7 +732,7 @@ return path def clear(self): -self._map = {} +self._map = dirstatemap() self._nonnormalset = set() self._otherparentset = set() if "_dirs" in self.__dict__: @@ -840,7 +841,8 @@ now = end # trust our estimate that the end is near now break -st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now)) +st.write(parsers.pack_dirstate(self._map._map, self._copymap, self._pl, + now)) self._nonnormalset, self._otherparentset = nonnormalentries(self._map) st.close() self._lastnormaltime = 0 @@ -979,7 +981,7 @@ results[nf] = None else: # does it match a missing directory? if alldirs is None: -alldirs = util.dirs(dmap) +alldirs = util.dirs(dmap._map) if nf in alldirs: if matchedir: matchedir(nf) @@ -1339,3 +1341,31 @@ def clearbackup(self, tr, backupname): '''Clear backup file''' self._opener.unlink(backupname) + +class dirstatemap(object): +def __init__(self): +self._map = {} + +def iteritems(self): +return self._map.iteritems() + +def __iter__(self): +return iter(self._map) + +def get(self, key, default=None): +return self._map.get(key, default) + +def __contains__(self, key): +return key in self._map + +def __setitem__(self, key, value): +self._map[key] = value + +def __getitem__(self, key): +return self._map[key] + +def __delitem__(self, key): +del self._map[key] + +def keys(self): +return self._map.keys() To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D757: dirstate: move opendirstatefile to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0407c611b7be: dirstate: move opendirstatefile to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D757?vs=2081=2160 REVISION DETAIL https://phab.mercurial-scm.org/D757 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -82,9 +82,6 @@ self._origpl = None self._updatedfiles = set() -# for consistent view between _pl() and _read() invocations -self._pendingmode = None - @contextlib.contextmanager def parentchange(self): '''Context manager for handling dirstate parents. @@ -190,7 +187,7 @@ @propertycache def _pl(self): try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() st = fp.read(40) fp.close() l = len(st) @@ -401,23 +398,14 @@ f.discard() raise -def _opendirstatefile(self): -fp, mode = txnutil.trypending(self._root, self._opener, self._filename) -if self._pendingmode is not None and self._pendingmode != mode: -fp.close() -raise error.Abort(_('working directory state may be ' -'changed parallelly')) -self._pendingmode = mode -return fp - def _read(self): -self._map = dirstatemap() +self._map = dirstatemap(self._ui, self._opener, self._root) # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() try: st = fp.read() finally: @@ -698,7 +686,7 @@ return path def clear(self): -self._map = dirstatemap() +self._map = dirstatemap(self._ui, self._opener, self._root) self._nonnormalset = set() self._otherparentset = set() if "_dirs" in self.__dict__: @@ -1308,10 +1296,18 @@ self._opener.unlink(backupname) class dirstatemap(object): -def __init__(self): +def __init__(self, ui, opener, root): +self._ui = ui +self._opener = opener +self._root = root +self._filename = 'dirstate' + self._map = {} self.copymap = {} +# for consistent view between _pl() and _read() invocations +self._pendingmode = None + def iteritems(self): return self._map.iteritems() @@ -1375,3 +1371,13 @@ current dirstate. """ return util.dirs(self._map, 'r') + +def _opendirstatefile(self): +fp, mode = txnutil.trypending(self._root, self._opener, self._filename) +if self._pendingmode is not None and self._pendingmode != mode: +fp.close() +raise error.Abort(_('working directory state may be ' +'changed parallelly')) +self._pendingmode = mode +return fp + To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D756: dirstate: move _copymap to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG894cd88815ca: dirstate: move _copymap to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D756?vs=2080=2159 REVISION DETAIL https://phab.mercurial-scm.org/D756 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -137,11 +137,6 @@ return self._map @propertycache -def _copymap(self): -self._read() -return self._copymap - -@propertycache def _identity(self): self._read() return self._identity @@ -378,13 +373,13 @@ # Discard 'm' markers when moving away from a merge state if s[0] == 'm': -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.normallookup(f) # Also fix up otherparent markers elif s[0] == 'n' and s[2] == -2: -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.add(f) @@ -418,7 +413,6 @@ def _read(self): self._map = dirstatemap() -self._copymap = {} # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) @@ -461,7 +455,7 @@ # # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map._map, self._copymap, st) +p = parse_dirstate(self._map._map, self._map.copymap, st) if not self._dirtypl: self._pl = p @@ -472,7 +466,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_copymap", "_identity", +for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", "_pl", "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): @@ -490,17 +484,17 @@ return self._dirty = True if source is not None: -self._copymap[dest] = source +self._map.copymap[dest] = source self._updatedfiles.add(source) self._updatedfiles.add(dest) -elif self._copymap.pop(dest, None): +elif self._map.copymap.pop(dest, None): self._updatedfiles.add(dest) def copied(self, file): -return self._copymap.get(file, None) +return self._map.copymap.get(file, None) def copies(self): -return self._copymap +return self._map.copymap def _droppath(self, f): if self[f] not in "?r" and "_dirs" in self.__dict__: @@ -543,7 +537,7 @@ mtime = s.st_mtime self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) if mtime > self._lastnormaltime: @@ -561,7 +555,7 @@ entry = self._map.get(f) if entry is not None: if entry[0] == 'r' and entry[2] in (-1, -2): -source = self._copymap.get(f) +source = self._map.copymap.get(f) if entry[2] == -1: self.merge(f) elif entry[2] == -2: @@ -572,7 +566,7 @@ if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: return self._addpath(f, 'n', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) @@ -587,12 +581,12 @@ else: # add-like self._addpath(f, 'n', 0, -2, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def add(self, f): '''Mark a file added.''' self._addpath(f, 'a', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def remove(self, f): '''Mark a file removed.''' @@ -611,7 +605,7 @@ self._map[f] = dirstatetuple('r', 0, size, 0) self._nonnormalset.add(f) if size == 0: -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def merge(self, f): '''Mark a file merged.''' @@ -627,7
D753: dirstate: move nonnormalentries to dirstatemap
This revision was automatically updated to reflect the committed changes. Closed by commit rHG362ed91ca00c: dirstate: move nonnormalentries to dirstatemap (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D753?vs=1948=2156 REVISION DETAIL https://phab.mercurial-scm.org/D753 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -54,20 +54,6 @@ os.close(tmpfd) vfs.unlink(tmpname) -def nonnormalentries(dmap): -'''Compute the nonnormal dirstate entries from the dmap''' -try: -return parsers.nonnormalotherparententries(dmap._map) -except AttributeError: -nonnorm = set() -otherparent = set() -for fname, e in dmap.iteritems(): -if e[0] != 'n' or e[3] == -1: -nonnorm.add(fname) -if e[0] == 'n' and e[2] == -2: -otherparent.add(fname) -return nonnorm, otherparent - class dirstate(object): def __init__(self, opener, ui, root, validate, sparsematchfn): @@ -162,13 +148,13 @@ @propertycache def _nonnormalset(self): -nonnorm, otherparents = nonnormalentries(self._map) +nonnorm, otherparents = self._map.nonnormalentries() self._otherparentset = otherparents return nonnorm @propertycache def _otherparentset(self): -nonnorm, otherparents = nonnormalentries(self._map) +nonnorm, otherparents = self._map.nonnormalentries() self._nonnormalset = nonnorm return otherparents @@ -843,7 +829,7 @@ st.write(parsers.pack_dirstate(self._map._map, self._copymap, self._pl, now)) -self._nonnormalset, self._otherparentset = nonnormalentries(self._map) +self._nonnormalset, self._otherparentset = self._map.nonnormalentries() st.close() self._lastnormaltime = 0 self._dirty = self._dirtypl = False @@ -1369,3 +1355,18 @@ def keys(self): return self._map.keys() + +def nonnormalentries(self): +'''Compute the nonnormal dirstate entries from the dmap''' +try: +return parsers.nonnormalotherparententries(self._map) +except AttributeError: +nonnorm = set() +otherparent = set() +for fname, e in self._map.iteritems(): +if e[0] != 'n' or e[3] == -1: +nonnorm.add(fname) +if e[0] == 'n' and e[2] == -2: +otherparent.add(fname) +return nonnorm, otherparent + To: durham, #hg-reviewers, indygreg Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D821: unamend: move fb extension unamend to core
durham requested changes to this revision. durham added a comment. This revision now requires changes to proceed. Generally looks good. Just need to fix the transaction thing. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D821 To: pulkit, #hg-reviewers, durham Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D821: unamend: move fb extension unamend to core
durham added inline comments. INLINE COMMENTS > uncommit.py:260 > + > +tr = repo.transaction('unamend') > +with dirstate.parentchange(): This should be in a with statement probably? Can we just have it be as part of the top level lock with statement? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D821 To: pulkit, #hg-reviewers Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D738: directaccess: add support to export and tests to demonstrate things
durham accepted this revision. durham added inline comments. INLINE COMMENTS > test-directaccess.t:11 > + > [extensions] > + > uncommit = > + > amend = Do we need uncommit? It doesn't seem used. > test-directaccess.t:64 > + > + $ hg exp 7 > + # HG changeset patch I'm still not sure we want to support rev numbers. I think we definitely don't want to support them for write commands, even recoverable ones. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D738 To: pulkit, #hg-reviewers, durham Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D737: directaccess: add support for accessing hidden commits if command is read only
durham requested changes to this revision. durham added a comment. This revision now requires changes to proceed. Probably needs a simple test, unless that comes later in the series. INLINE COMMENTS > dispatch.py:898 > +if cmdtype == readonly: > +repo = repo.unfiltered() > args.insert(0, repo) This seems overly broad. 'hg log' is a readonly command, and if I do 'hg log -r HIDDEN_HASH' I want to see the hidden commit, but if I do 'hg log' I don't want to see hidden commits. I think this change would cause hg log to show all hidden commits right? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D737 To: pulkit, #hg-reviewers, durham Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D736: directaccess: add support for storing the type of command in func object
durham accepted this revision. durham added a comment. Minor comments, but lgtm INLINE COMMENTS > registrar.py:143 > +unrecoverablewrite = "unrecoverable" > +recoverablewrite = "recoverable" > +readonly = "readonly" Might be worth a comment describing what these mean, since it's not obvious without context. > registrar.py:150 > + > +possiblecmdtypes = set(["unrecoverable", "recoverable", "readonly"]) > +if cmdtype not in possiblecmdtypes: These should probably refer to the class constants instead of hard coding duplicate strings. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D736 To: pulkit, #hg-reviewers, durham Cc: durham, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D728: rebase: move working parent movement logic to scmutil.cleanupnodes
durham requested changes to this revision. durham added inline comments. This revision now requires changes to proceed. INLINE COMMENTS > scmutil.py:603 > +changes will be discarded, the caller is responsible for checking the > +cleanness of working copy. > """ This seems like a bit of a bizarre feature for an api about cleaning up nodes. Maybe cleanup nodes should return a mapping of before-to-after and the caller can use that to perform an update if they need to? I'd rather have APIs that can be composed, than add new permutations to existing APIs. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D728 To: quark, #hg-reviewers, durham Cc: durham, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D758: dirstate: move parent reading to the dirstatemap class
durham added inline comments. INLINE COMMENTS > indygreg wrote in dirstate.py:1370 > Nit: this should ideally be in a context manager or try..finally block. But > it existed this way before, so not worth blocking over. Left this alone for now to avoid code churn during the refactor. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D758 To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D757: dirstate: move opendirstatefile to dirstatemap
durham marked an inline comment as done. durham added inline comments. INLINE COMMENTS > indygreg wrote in dirstate.py:1373-1374 > This (pre-existing) error message is pretty bad because typical end users > won't have a clue what it means or what to do if they see it. Agreed. I'm not even sure what it's actually saying as a mercurial developer... REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D757 To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D756: dirstate: move _copymap to dirstatemap
durham updated this revision to Diff 2080. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D756?vs=1951=2080 REVISION DETAIL https://phab.mercurial-scm.org/D756 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -137,11 +137,6 @@ return self._map @propertycache -def _copymap(self): -self._read() -return self._copymap - -@propertycache def _identity(self): self._read() return self._identity @@ -378,13 +373,13 @@ # Discard 'm' markers when moving away from a merge state if s[0] == 'm': -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.normallookup(f) # Also fix up otherparent markers elif s[0] == 'n' and s[2] == -2: -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.add(f) @@ -418,7 +413,6 @@ def _read(self): self._map = dirstatemap() -self._copymap = {} # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) @@ -461,7 +455,7 @@ # # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map._map, self._copymap, st) +p = parse_dirstate(self._map._map, self._map.copymap, st) if not self._dirtypl: self._pl = p @@ -472,7 +466,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_copymap", "_identity", +for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", "_pl", "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): @@ -490,17 +484,17 @@ return self._dirty = True if source is not None: -self._copymap[dest] = source +self._map.copymap[dest] = source self._updatedfiles.add(source) self._updatedfiles.add(dest) -elif self._copymap.pop(dest, None): +elif self._map.copymap.pop(dest, None): self._updatedfiles.add(dest) def copied(self, file): -return self._copymap.get(file, None) +return self._map.copymap.get(file, None) def copies(self): -return self._copymap +return self._map.copymap def _droppath(self, f): if self[f] not in "?r" and "_dirs" in self.__dict__: @@ -543,7 +537,7 @@ mtime = s.st_mtime self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) if mtime > self._lastnormaltime: @@ -561,7 +555,7 @@ entry = self._map.get(f) if entry is not None: if entry[0] == 'r' and entry[2] in (-1, -2): -source = self._copymap.get(f) +source = self._map.copymap.get(f) if entry[2] == -1: self.merge(f) elif entry[2] == -2: @@ -572,7 +566,7 @@ if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: return self._addpath(f, 'n', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) @@ -587,12 +581,12 @@ else: # add-like self._addpath(f, 'n', 0, -2, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def add(self, f): '''Mark a file added.''' self._addpath(f, 'a', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def remove(self, f): '''Mark a file removed.''' @@ -611,7 +605,7 @@ self._map[f] = dirstatetuple('r', 0, size, 0) self._nonnormalset.add(f) if size == 0: -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def merge(self, f): '''Mark a file merged.''' @@ -627,7 +621,7 @@ del self._map[f] if f in self._nonnormalset: self._nonnormalset.remove(f) -
D759: dirstate: move parents source of truth to dirstatemap
durham updated this revision to Diff 2083. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D759?vs=1954=2083 REVISION DETAIL https://phab.mercurial-scm.org/D759 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -71,7 +71,6 @@ # UNC path pointing to root share (issue4557) self._rootdir = pathutil.normasprefix(root) self._dirty = False -self._dirtypl = False self._lastnormaltime = 0 self._ui = ui self._filecache = {} @@ -184,7 +183,7 @@ raise return "default" -@propertycache +@property def _pl(self): return self._map.parents() @@ -343,11 +342,11 @@ raise ValueError("cannot set dirstate parent without " "calling dirstate.beginparentchange") -self._dirty = self._dirtypl = True +self._dirty = True oldp2 = self._pl[1] if self._origpl is None: self._origpl = self._pl -self._pl = p1, p2 +self._map.setparents(p1, p2) copies = {} if oldp2 != nullid and p2 == nullid: candidatefiles = self._nonnormalset.union(self._otherparentset) @@ -432,8 +431,8 @@ # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) p = parse_dirstate(self._map._map, self._map.copymap, st) -if not self._dirtypl: -self._pl = p +if not self._map._dirtyparents: +self._map.setparents(*p) def invalidate(self): '''Causes the next access to reread the dirstate. @@ -444,7 +443,7 @@ for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", - "_pl", "_dirs", "_ignore", "_nonnormalset", + "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): if a in self.__dict__: delattr(self, a) @@ -679,7 +678,7 @@ self._otherparentset = set() if "_dirs" in self.__dict__: delattr(self, "_dirs") -self._pl = [nullid, nullid] +self._map.setparents(nullid, nullid) self._lastnormaltime = 0 self._updatedfiles.clear() self._dirty = True @@ -694,7 +693,7 @@ if self._origpl is None: self._origpl = self._pl -self._pl = (parent, nullid) +self._map.setparents(parent, nullid) for f in changedfiles: if f in allfiles: self.normallookup(f) @@ -787,7 +786,7 @@ self._nonnormalset, self._otherparentset = self._map.nonnormalentries() st.close() self._lastnormaltime = 0 -self._dirty = self._dirtypl = False +self._dirty = self._map._dirtyparents = False def _dirignore(self, f): if f == '.': @@ -1292,6 +1291,8 @@ self._map = {} self.copymap = {} +self._parents = None +self._dirtyparents = False # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -1370,16 +1371,28 @@ return fp def parents(self): -try: -fp = self._opendirstatefile() -st = fp.read(40) -fp.close() +if not self._parents: +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +except IOError as err: +if err.errno != errno.ENOENT: +raise +# File doesn't exist, so the current state is empty +st = '' + l = len(st) if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +self._parents = st[:20], st[20:40] +elif l == 0: +self._parents = [nullid, nullid] +else: +raise error.Abort(_('working directory state appears ' +'damaged!')) + +return self._parents + +def setparents(self, p1, p2): +self._parents = (p1, p2) +self._dirtyparents = True To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D757: dirstate: move opendirstatefile to dirstatemap
durham updated this revision to Diff 2081. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D757?vs=1952=2081 REVISION DETAIL https://phab.mercurial-scm.org/D757 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -82,9 +82,6 @@ self._origpl = None self._updatedfiles = set() -# for consistent view between _pl() and _read() invocations -self._pendingmode = None - @contextlib.contextmanager def parentchange(self): '''Context manager for handling dirstate parents. @@ -190,7 +187,7 @@ @propertycache def _pl(self): try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() st = fp.read(40) fp.close() l = len(st) @@ -401,23 +398,14 @@ f.discard() raise -def _opendirstatefile(self): -fp, mode = txnutil.trypending(self._root, self._opener, self._filename) -if self._pendingmode is not None and self._pendingmode != mode: -fp.close() -raise error.Abort(_('working directory state may be ' -'changed parallelly')) -self._pendingmode = mode -return fp - def _read(self): -self._map = dirstatemap() +self._map = dirstatemap(self._ui, self._opener, self._root) # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() try: st = fp.read() finally: @@ -698,7 +686,7 @@ return path def clear(self): -self._map = dirstatemap() +self._map = dirstatemap(self._ui, self._opener, self._root) self._nonnormalset = set() self._otherparentset = set() if "_dirs" in self.__dict__: @@ -1308,10 +1296,18 @@ self._opener.unlink(backupname) class dirstatemap(object): -def __init__(self): +def __init__(self, ui, opener, root): +self._ui = ui +self._opener = opener +self._root = root +self._filename = 'dirstate' + self._map = {} self.copymap = {} +# for consistent view between _pl() and _read() invocations +self._pendingmode = None + def iteritems(self): return self._map.iteritems() @@ -1375,3 +1371,13 @@ current dirstate. """ return util.dirs(self._map, 'r') + +def _opendirstatefile(self): +fp, mode = txnutil.trypending(self._root, self._opener, self._filename) +if self._pendingmode is not None and self._pendingmode != mode: +fp.close() +raise error.Abort(_('working directory state may be ' +'changed parallelly')) +self._pendingmode = mode +return fp + To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D758: dirstate: move parent reading to the dirstatemap class
durham updated this revision to Diff 2082. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D758?vs=1953=2082 REVISION DETAIL https://phab.mercurial-scm.org/D758 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -186,19 +186,7 @@ @propertycache def _pl(self): -try: -fp = self._map._opendirstatefile() -st = fp.read(40) -fp.close() -l = len(st) -if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +return self._map.parents() @propertycache def _dirs(self): @@ -1381,3 +1369,17 @@ self._pendingmode = mode return fp +def parents(self): +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +l = len(st) +if l == 40: +return st[:20], st[20:40] +elif l > 0 and l < 40: +raise error.Abort(_('working directory state appears damaged!')) +except IOError as err: +if err.errno != errno.ENOENT: +raise +return [nullid, nullid] To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D755: dirstate: move _dirs to dirstatemap
durham updated this revision to Diff 2079. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D755?vs=1950=2079 REVISION DETAIL https://phab.mercurial-scm.org/D755 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -210,7 +210,7 @@ @propertycache def _dirs(self): -return util.dirs(self._map._map, 'r') +return self._map.dirs() def dirs(self): return self._dirs @@ -1375,3 +1375,9 @@ f[normcase(name)] = name f['.'] = '.' # prevents useless util.fspath() invocation return f + +def dirs(self): +"""Returns a set-like object containing all the directories in the +current dirstate. +""" +return util.dirs(self._map, 'r') To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D754: dirstate: move filefoldmap to dirstatemap
durham updated this revision to Diff 2078. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D754?vs=1949=2078 REVISION DETAIL https://phab.mercurial-scm.org/D754 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -160,21 +160,7 @@ @propertycache def _filefoldmap(self): -try: -makefilefoldmap = parsers.make_file_foldmap -except AttributeError: -pass -else: -return makefilefoldmap(self._map._map, util.normcasespec, - util.normcasefallback) - -f = {} -normcase = util.normcase -for name, s in self._map.iteritems(): -if s[0] != 'r': -f[normcase(name)] = name -f['.'] = '.' # prevents useless util.fspath() invocation -return f +return self._map.filefoldmap() @propertycache def _dirfoldmap(self): @@ -1370,3 +1356,22 @@ otherparent.add(fname) return nonnorm, otherparent +def filefoldmap(self): +"""Returns a dictionary mapping normalized case paths to their +non-normalized versions. +""" +try: +makefilefoldmap = parsers.make_file_foldmap +except AttributeError: +pass +else: +return makefilefoldmap(self._map, util.normcasespec, + util.normcasefallback) + +f = {} +normcase = util.normcase +for name, s in self._map.iteritems(): +if s[0] != 'r': +f[normcase(name)] = name +f['.'] = '.' # prevents useless util.fspath() invocation +return f To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D752: dirstate: create new dirstatemap class
durham added inline comments. INLINE COMMENTS > indygreg wrote in dirstate.py:1345 > Do we want this inherit from `collections.MutableMapping`? > > A good reason to say "no" is KISS, especially if we'll be having multiple > backends. For now I'd say no, until we've done at least one other backend implementation and have seen that MutableMapping is still appropriate. In particular, we might not want to allow iteration without a matcher, to discourage O(working copy) operations. > indygreg wrote in dirstate.py:1349-1350 > Given that `iteritems()` no longer exists in Python 3, should we change this > to `items()` and standardize on the modern convention? Do we do that anywhere else in the code base already? My initial tendency is to just follow existing patterns, especially during a refactor, to minimize churn. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D752 To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D752: dirstate: create new dirstatemap class
durham added a comment. This series is the first 8 commits of a 20 patch series. At the end all the storage for the dirstate goes through the dirstatemap class. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D752 To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D758: dirstate: move parent reading to the dirstatemap class
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving dirstate storage logic to a separate class, let's move the function that reads the parents from the file. This will allow extensions to write dirstate's that store the parents in other ways. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D758 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -186,19 +186,7 @@ @propertycache def _pl(self): -try: -fp = self._map._opendirstatefile() -st = fp.read(40) -fp.close() -l = len(st) -if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +return self._map.parents() @propertycache def _dirs(self): @@ -1375,3 +1363,17 @@ self._pendingmode = mode return fp +def parents(self): +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +l = len(st) +if l == 40: +return st[:20], st[20:40] +elif l > 0 and l < 40: +raise error.Abort(_('working directory state appears damaged!')) +except IOError as err: +if err.errno != errno.ENOENT: +raise +return [nullid, nullid] To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D754: dirstate: move filefoldmap to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving the dirstate storage logic to a separate class, lets move the filfoldmap computation to that class. This will allow extensions to replace the dirstate storage with something that persists the filefoldmap. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D754 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -160,21 +160,7 @@ @propertycache def _filefoldmap(self): -try: -makefilefoldmap = parsers.make_file_foldmap -except AttributeError: -pass -else: -return makefilefoldmap(self._map._map, util.normcasespec, - util.normcasefallback) - -f = {} -normcase = util.normcase -for name, s in self._map.iteritems(): -if s[0] != 'r': -f[normcase(name)] = name -f['.'] = '.' # prevents useless util.fspath() invocation -return f +return self._map.filefoldmap() @propertycache def _dirfoldmap(self): @@ -1370,3 +1356,19 @@ otherparent.add(fname) return nonnorm, otherparent +def filefoldmap(self): +try: +makefilefoldmap = parsers.make_file_foldmap +except AttributeError: +pass +else: +return makefilefoldmap(self._map, util.normcasespec, + util.normcasefallback) + +f = {} +normcase = util.normcase +for name, s in self._map.iteritems(): +if s[0] != 'r': +f[normcase(name)] = name +f['.'] = '.' # prevents useless util.fspath() invocation +return f To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D759: dirstate: move parents source of truth to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving dirstate storage to its own class, let's move the source of truth for the dirstate parents to dirstatemap. This requires that dirstate._pl no longer be a cache, and that all sets go through dirstatemap.setparents. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D759 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -71,7 +71,6 @@ # UNC path pointing to root share (issue4557) self._rootdir = pathutil.normasprefix(root) self._dirty = False -self._dirtypl = False self._lastnormaltime = 0 self._ui = ui self._filecache = {} @@ -184,7 +183,7 @@ raise return "default" -@propertycache +@property def _pl(self): return self._map.parents() @@ -343,11 +342,11 @@ raise ValueError("cannot set dirstate parent without " "calling dirstate.beginparentchange") -self._dirty = self._dirtypl = True +self._dirty = True oldp2 = self._pl[1] if self._origpl is None: self._origpl = self._pl -self._pl = p1, p2 +self._map.setparents(p1, p2) copies = {} if oldp2 != nullid and p2 == nullid: candidatefiles = self._nonnormalset.union(self._otherparentset) @@ -432,8 +431,8 @@ # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) p = parse_dirstate(self._map._map, self._map.copymap, st) -if not self._dirtypl: -self._pl = p +if not self._map._dirtyparents: +self._map.setparents(*p) def invalidate(self): '''Causes the next access to reread the dirstate. @@ -444,7 +443,7 @@ for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", - "_pl", "_dirs", "_ignore", "_nonnormalset", + "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): if a in self.__dict__: delattr(self, a) @@ -679,7 +678,7 @@ self._otherparentset = set() if "_dirs" in self.__dict__: delattr(self, "_dirs") -self._pl = [nullid, nullid] +self._map.setparents(nullid, nullid) self._lastnormaltime = 0 self._updatedfiles.clear() self._dirty = True @@ -694,7 +693,7 @@ if self._origpl is None: self._origpl = self._pl -self._pl = (parent, nullid) +self._map.setparents(parent, nullid) for f in changedfiles: if f in allfiles: self.normallookup(f) @@ -787,7 +786,7 @@ self._nonnormalset, self._otherparentset = self._map.nonnormalentries() st.close() self._lastnormaltime = 0 -self._dirty = self._dirtypl = False +self._dirty = self._map._dirtyparents = False def _dirignore(self, f): if f == '.': @@ -1292,6 +1291,8 @@ self._map = {} self.copymap = {} +self._parents = None +self._dirtyparents = False # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -1364,16 +1365,25 @@ return fp def parents(self): -try: -fp = self._opendirstatefile() -st = fp.read(40) -fp.close() -l = len(st) -if l == 40: -return st[:20], st[20:40] -elif l > 0 and l < 40: -raise error.Abort(_('working directory state appears damaged!')) -except IOError as err: -if err.errno != errno.ENOENT: -raise -return [nullid, nullid] +if not self._parents: +try: +fp = self._opendirstatefile() +st = fp.read(40) +fp.close() +l = len(st) +if l == 40: +self._parents = st[:20], st[20:40] +elif l > 0 and l < 40: +raise error.Abort(_('working directory state appears ' +'damaged!')) +except IOError as err: +if err.errno != errno.ENOENT: +raise +if not self._parents: +self._parents = [nullid, nullid] + +return self._parents + +def setparents(self, p1, p2): +self._parents = (p1, p2) +self._dirtyparents = True To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list
D752: dirstate: create new dirstatemap class
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This is part of a larger refactor to move the dirstate storage logic to a separate class, so it's easier to rewrite the dirstate storage layer without having to rewrite all the algorithms as well. Step one it to create the class, and replace dirstate._map with it. The abstraction bleeds through in a few places where the main dirstate class has to access self._map._map, but those will be cleaned up in future patches. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D752 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -57,7 +57,7 @@ def nonnormalentries(dmap): '''Compute the nonnormal dirstate entries from the dmap''' try: -return parsers.nonnormalotherparententries(dmap) +return parsers.nonnormalotherparententries(dmap._map) except AttributeError: nonnorm = set() otherparent = set() @@ -179,7 +179,7 @@ except AttributeError: pass else: -return makefilefoldmap(self._map, util.normcasespec, +return makefilefoldmap(self._map._map, util.normcasespec, util.normcasefallback) f = {} @@ -238,7 +238,7 @@ @propertycache def _dirs(self): -return util.dirs(self._map, 'r') +return util.dirs(self._map._map, 'r') def dirs(self): return self._dirs @@ -444,7 +444,8 @@ return fp def _read(self): -self._map = {} +self._map = dirstatemap() + self._copymap = {} # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( @@ -473,7 +474,7 @@ # This heuristic is imperfect in many ways, so in a future dirstate # format update it makes sense to just record the number of entries # on write. -self._map = parsers.dict_new_presized(len(st) / 71) +self._map._map = parsers.dict_new_presized(len(st) / 71) # Python's garbage collector triggers a GC each time a certain number # of container objects (the number being defined by @@ -488,7 +489,7 @@ # # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map, self._copymap, st) +p = parse_dirstate(self._map._map, self._copymap, st) if not self._dirtypl: self._pl = p @@ -731,7 +732,7 @@ return path def clear(self): -self._map = {} +self._map = dirstatemap() self._nonnormalset = set() self._otherparentset = set() if "_dirs" in self.__dict__: @@ -840,7 +841,8 @@ now = end # trust our estimate that the end is near now break -st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now)) +st.write(parsers.pack_dirstate(self._map._map, self._copymap, self._pl, + now)) self._nonnormalset, self._otherparentset = nonnormalentries(self._map) st.close() self._lastnormaltime = 0 @@ -979,7 +981,7 @@ results[nf] = None else: # does it match a missing directory? if alldirs is None: -alldirs = util.dirs(dmap) +alldirs = util.dirs(dmap._map) if nf in alldirs: if matchedir: matchedir(nf) @@ -1339,3 +1341,31 @@ def clearbackup(self, tr, backupname): '''Clear backup file''' self._opener.unlink(backupname) + +class dirstatemap(object): +def __init__(self): +self._map = {} + +def iteritems(self): +return self._map.iteritems() + +def __iter__(self): +return iter(self._map) + +def get(self, key, default=None): +return self._map.get(key, default) + +def __contains__(self, key): +return key in self._map + +def __setitem__(self, key, value): +self._map[key] = value + +def __getitem__(self, key): +return self._map[key] + +def __delitem__(self, key): +del self._map[key] + +def keys(self): +return self._map.keys() To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D757: dirstate: move opendirstatefile to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving the dirstate storage logic to another class, let's move opendirstatefile to dirstatemap. This will allow extensions to replace the pending abstraction. Future patches will move the consumers of _opendirstatefile into dirstatemap as well. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D757 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -82,9 +82,6 @@ self._origpl = None self._updatedfiles = set() -# for consistent view between _pl() and _read() invocations -self._pendingmode = None - @contextlib.contextmanager def parentchange(self): '''Context manager for handling dirstate parents. @@ -190,7 +187,7 @@ @propertycache def _pl(self): try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() st = fp.read(40) fp.close() l = len(st) @@ -401,23 +398,14 @@ f.discard() raise -def _opendirstatefile(self): -fp, mode = txnutil.trypending(self._root, self._opener, self._filename) -if self._pendingmode is not None and self._pendingmode != mode: -fp.close() -raise error.Abort(_('working directory state may be ' -'changed parallelly')) -self._pendingmode = mode -return fp - def _read(self): -self._map = dirstatemap() +self._map = dirstatemap(self._opener, self._ui, self._root) # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) try: -fp = self._opendirstatefile() +fp = self._map._opendirstatefile() try: st = fp.read() finally: @@ -698,7 +686,7 @@ return path def clear(self): -self._map = dirstatemap() +self._map = dirstatemap(self._opener, self._ui, self._root) self._nonnormalset = set() self._otherparentset = set() if "_dirs" in self.__dict__: @@ -1308,10 +1296,18 @@ self._opener.unlink(backupname) class dirstatemap(object): -def __init__(self): +def __init__(self, opener, ui, root): +self._opener = opener +self._ui = ui +self._root = root +self._filename = 'dirstate' + self._map = {} self.copymap = {} +# for consistent view between _pl() and _read() invocations +self._pendingmode = None + def iteritems(self): return self._map.iteritems() @@ -1369,3 +1365,13 @@ def dirs(self): return util.dirs(self._map, 'r') + +def _opendirstatefile(self): +fp, mode = txnutil.trypending(self._root, self._opener, self._filename) +if self._pendingmode is not None and self._pendingmode != mode: +fp.close() +raise error.Abort(_('working directory state may be ' +'changed parallelly')) +self._pendingmode = mode +return fp + To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D756: dirstate: move _copymap to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving all dirstate storage to a new class, let's move the copymap onto that class. In a future patch this will let us move the read/write functions to the dirstatemap class, and for extensions this lets us replace the copy storage with alternative storage. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D756 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -137,11 +137,6 @@ return self._map @propertycache -def _copymap(self): -self._read() -return self._copymap - -@propertycache def _identity(self): self._read() return self._identity @@ -378,13 +373,13 @@ # Discard 'm' markers when moving away from a merge state if s[0] == 'm': -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.normallookup(f) # Also fix up otherparent markers elif s[0] == 'n' and s[2] == -2: -source = self._copymap.get(f) +source = self._map.copymap.get(f) if source: copies[f] = source self.add(f) @@ -418,7 +413,6 @@ def _read(self): self._map = dirstatemap() -self._copymap = {} # ignore HG_PENDING because identity is used only for writing self._identity = util.filestat.frompath( self._opener.join(self._filename)) @@ -461,7 +455,7 @@ # # (we cannot decorate the function directly since it is in a C module) parse_dirstate = util.nogc(parsers.parse_dirstate) -p = parse_dirstate(self._map._map, self._copymap, st) +p = parse_dirstate(self._map._map, self._map.copymap, st) if not self._dirtypl: self._pl = p @@ -472,7 +466,7 @@ rereads the dirstate. Use localrepo.invalidatedirstate() if you want to check whether the dirstate has changed before rereading it.''' -for a in ("_map", "_copymap", "_identity", +for a in ("_map", "_identity", "_filefoldmap", "_dirfoldmap", "_branch", "_pl", "_dirs", "_ignore", "_nonnormalset", "_otherparentset"): @@ -490,17 +484,17 @@ return self._dirty = True if source is not None: -self._copymap[dest] = source +self._map.copymap[dest] = source self._updatedfiles.add(source) self._updatedfiles.add(dest) -elif self._copymap.pop(dest, None): +elif self._map.copymap.pop(dest, None): self._updatedfiles.add(dest) def copied(self, file): -return self._copymap.get(file, None) +return self._map.copymap.get(file, None) def copies(self): -return self._copymap +return self._map.copymap def _droppath(self, f): if self[f] not in "?r" and "_dirs" in self.__dict__: @@ -543,7 +537,7 @@ mtime = s.st_mtime self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) if mtime > self._lastnormaltime: @@ -561,7 +555,7 @@ entry = self._map.get(f) if entry is not None: if entry[0] == 'r' and entry[2] in (-1, -2): -source = self._copymap.get(f) +source = self._map.copymap.get(f) if entry[2] == -1: self.merge(f) elif entry[2] == -2: @@ -572,7 +566,7 @@ if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: return self._addpath(f, 'n', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) if f in self._nonnormalset: self._nonnormalset.remove(f) @@ -587,12 +581,12 @@ else: # add-like self._addpath(f, 'n', 0, -2, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def add(self, f): '''Mark a file added.''' self._addpath(f, 'a', 0, -1, -1) -self._copymap.pop(f, None) +self._map.copymap.pop(f, None) def remove(self, f): '''Mark a file removed.''' @@ -611,7 +605,7 @@ self._map[f] = dirstatetuple('r', 0, size, 0) self._nonnormalset.add(f) if size == 0: -
D753: dirstate: move nonnormalentries to dirstatemap
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of moving dirstate storage to its own class, let's move the nonnormalentries logic onto the dirstatemap class. This will let extensions replace the nonnormalentries logic with a persisted cache. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D753 AFFECTED FILES mercurial/dirstate.py CHANGE DETAILS diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -54,20 +54,6 @@ os.close(tmpfd) vfs.unlink(tmpname) -def nonnormalentries(dmap): -'''Compute the nonnormal dirstate entries from the dmap''' -try: -return parsers.nonnormalotherparententries(dmap._map) -except AttributeError: -nonnorm = set() -otherparent = set() -for fname, e in dmap.iteritems(): -if e[0] != 'n' or e[3] == -1: -nonnorm.add(fname) -if e[0] == 'n' and e[2] == -2: -otherparent.add(fname) -return nonnorm, otherparent - class dirstate(object): def __init__(self, opener, ui, root, validate, sparsematchfn): @@ -162,13 +148,13 @@ @propertycache def _nonnormalset(self): -nonnorm, otherparents = nonnormalentries(self._map) +nonnorm, otherparents = self._map.nonnormalentries() self._otherparentset = otherparents return nonnorm @propertycache def _otherparentset(self): -nonnorm, otherparents = nonnormalentries(self._map) +nonnorm, otherparents = self._map.nonnormalentries() self._nonnormalset = nonnorm return otherparents @@ -843,7 +829,7 @@ st.write(parsers.pack_dirstate(self._map._map, self._copymap, self._pl, now)) -self._nonnormalset, self._otherparentset = nonnormalentries(self._map) +self._nonnormalset, self._otherparentset = self._map.nonnormalentries() st.close() self._lastnormaltime = 0 self._dirty = self._dirtypl = False @@ -1369,3 +1355,18 @@ def keys(self): return self._map.keys() + +def nonnormalentries(self): +'''Compute the nonnormal dirstate entries from the dmap''' +try: +return parsers.nonnormalotherparententries(self._map) +except AttributeError: +nonnorm = set() +otherparent = set() +for fname, e in self._map.iteritems(): +if e[0] != 'n' or e[3] == -1: +nonnorm.add(fname) +if e[0] == 'n' and e[2] == -2: +otherparent.add(fname) +return nonnorm, otherparent + To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D730: revlog: add revmap back to revlog.addgroup
This revision was automatically updated to reflect the committed changes. durham marked an inline comment as done. Closed by commit rHG1db9abf407c5: revlog: add revmap back to revlog.addgroup (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D730?vs=1888=1942 REVISION DETAIL https://phab.mercurial-scm.org/D730 AFFECTED FILES mercurial/changegroup.py mercurial/revlog.py tests/test-revlog-raw.py CHANGE DETAILS diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py --- a/tests/test-revlog-raw.py +++ b/tests/test-revlog-raw.py @@ -119,7 +119,7 @@ 'deltabase': rlog.node(deltaparent), 'delta': rlog.revdiff(deltaparent, r)} -def deltaiter(self, linkmapper): +def deltaiter(self): chain = None for chunkdata in iter(lambda: self.deltachunk(chain), {}): node = chunkdata['node'] @@ -130,17 +130,16 @@ delta = chunkdata['delta'] flags = chunkdata['flags'] -link = linkmapper(cs) chain = node -yield (node, p1, p2, link, deltabase, delta, flags) +yield (node, p1, p2, cs, deltabase, delta, flags) def linkmap(lnode): return rlog.rev(lnode) dlog = newrevlog(destname, recreate=True) -dummydeltas = dummychangegroup().deltaiter(linkmap) -dlog.addgroup(dummydeltas, tr) +dummydeltas = dummychangegroup().deltaiter() +dlog.addgroup(dummydeltas, linkmap, tr) return dlog def lowlevelcopy(rlog, tr, destname=b'_destrevlog.i'): diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1910,7 +1910,7 @@ ifh.write(data[1]) self.checkinlinesize(transaction, ifh) -def addgroup(self, deltas, transaction, addrevisioncb=None): +def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None): """ add a delta group @@ -1944,7 +1944,8 @@ try: # loop through our set of deltas for data in deltas: -node, p1, p2, link, deltabase, delta, flags = data +node, p1, p2, linknode, deltabase, delta, flags = data +link = linkmapper(linknode) flags = flags or REVIDX_DEFAULT_FLAGS nodes.append(node) diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -245,8 +245,8 @@ # no new manifest will be created and the manifest group will # be empty during the pull self.manifestheader() -deltas = self.deltaiter(revmap) -repo.manifestlog._revlog.addgroup(deltas, trp) +deltas = self.deltaiter() +repo.manifestlog._revlog.addgroup(deltas, revmap, trp) repo.ui.progress(_('manifests'), None) self.callback = None @@ -308,8 +308,8 @@ efiles.update(cl.readfiles(node)) self.changelogheader() -deltas = self.deltaiter(csmap) -cgnodes = cl.addgroup(deltas, trp, addrevisioncb=onchangelog) +deltas = self.deltaiter() +cgnodes = cl.addgroup(deltas, csmap, trp, addrevisioncb=onchangelog) efiles = len(efiles) if not cgnodes: @@ -430,7 +430,7 @@ ret = deltaheads + 1 return ret -def deltaiter(self, linkmapper): +def deltaiter(self): """ returns an iterator of the deltas in this changegroup @@ -446,10 +446,9 @@ delta = chunkdata['delta'] flags = chunkdata['flags'] -link = linkmapper(cs) chain = node -yield (node, p1, p2, link, deltabase, delta, flags) +yield (node, p1, p2, cs, deltabase, delta, flags) class cg2unpacker(cg1unpacker): """Unpacker for cg2 streams. @@ -491,8 +490,8 @@ d = chunkdata["filename"] repo.ui.debug("adding %s revisions\n" % d) dirlog = repo.manifestlog._revlog.dirlog(d) -deltas = self.deltaiter(revmap) -if not dirlog.addgroup(deltas, trp): +deltas = self.deltaiter() +if not dirlog.addgroup(deltas, revmap, trp): raise error.Abort(_("received dir revlog group is empty")) class headerlessfixup(object): @@ -983,8 +982,8 @@ fl = repo.file(f) o = len(fl) try: -deltas = source.deltaiter(revmap) -if not fl.addgroup(deltas, trp): +deltas = source.deltaiter() +if not fl.addgroup(deltas, revmap, trp): raise error.Abort(_("received file revlog group is empty")) except error.CensoredBaseError as e: raise error.Abort(_("received delta base is censored: %s") % e) To: durham, indygreg,
D746: changegroup: remove dictionary creation from deltachunk
This revision was automatically updated to reflect the committed changes. Closed by commit rHG05131c963767: changegroup: remove dictionary creation from deltachunk (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D746?vs=1934=1945 REVISION DETAIL https://phab.mercurial-scm.org/D746 AFFECTED FILES mercurial/changegroup.py CHANGE DETAILS diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -188,8 +188,7 @@ header = struct.unpack(self.deltaheader, headerdata) delta = readexactly(self._stream, l - self.deltaheadersize) node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode) -return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs, -'deltabase': deltabase, 'delta': delta, 'flags': flags} +return (node, p1, p2, cs, deltabase, delta, flags) def getchunks(self): """returns all the chunks contains in the bundle @@ -438,17 +437,9 @@ """ chain = None for chunkdata in iter(lambda: self.deltachunk(chain), {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] -flags = chunkdata['flags'] - -chain = node - -yield (node, p1, p2, cs, deltabase, delta, flags) +# Chunkdata: (node, p1, p2, cs, deltabase, delta, flags) +yield chunkdata +chain = chunkdata[0] class cg2unpacker(cg1unpacker): """Unpacker for cg2 streams. To: durham, #hg-reviewers, martinvonz Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D745: bundlerepo: update to use new deltaiter api
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0fe62d8bdd50: bundlerepo: update to use new deltaiter api (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D745?vs=1933=1944 REVISION DETAIL https://phab.mercurial-scm.org/D745 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -55,25 +55,16 @@ self.bundle = bundle n = len(self) self.repotiprev = n - 1 -chain = None self.bundlerevs = set() # used by 'bundle()' revset expression -getchunk = lambda: bundle.deltachunk(chain) -for chunkdata in iter(getchunk, {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] -flags = chunkdata['flags'] +for deltadata in bundle.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata size = len(delta) start = bundle.tell() - size link = linkmapper(cs) if node in self.nodemap: # this can happen if two branches make the same change -chain = node self.bundlerevs.add(self.nodemap[node]) continue @@ -93,7 +84,6 @@ self.index.insert(-1, e) self.nodemap[node] = n self.bundlerevs.add(n) -chain = node n += 1 def _chunk(self, rev): To: durham, #hg-reviewers, martinvonz Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D744: debug: update debugbundle to use new deltaiter api
This revision was automatically updated to reflect the committed changes. Closed by commit rHG311f6ccf8f23: debug: update debugbundle to use new deltaiter api (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D744?vs=1932=1943 REVISION DETAIL https://phab.mercurial-scm.org/D744 AFFECTED FILES mercurial/debugcommands.py CHANGE DETAILS diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -263,18 +263,11 @@ def showchunks(named): ui.write("\n%s%s\n" % (indent_string, named)) -chain = None -for chunkdata in iter(lambda: gen.deltachunk(chain), {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] +for deltadata in gen.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata ui.write("%s%s %s %s %s %s %s\n" % (indent_string, hex(node), hex(p1), hex(p2), hex(cs), hex(deltabase), len(delta))) -chain = node chunkdata = gen.changelogheader() showchunks("changelog") @@ -287,11 +280,9 @@ if isinstance(gen, bundle2.unbundle20): raise error.Abort(_('use debugbundle2 for this file')) chunkdata = gen.changelogheader() -chain = None -for chunkdata in iter(lambda: gen.deltachunk(chain), {}): -node = chunkdata['node'] +for deltadata in gen.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata ui.write("%s%s\n" % (indent_string, hex(node))) -chain = node def _debugobsmarkers(ui, part, indent=0, **opts): """display version and markers contained in 'data'""" To: durham, #hg-reviewers, martinvonz Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D730: revlog: add revmap back to revlog.addgroup
durham marked an inline comment as done. durham added a comment. I've updated the message and attached 3 more commits to the stack that update debugbundle, bundlerepo, and the deltachunk fix you suggested. INLINE COMMENTS > martinvonz wrote in changegroup.py:191-192 > I'll repeat my comment from https://phab.mercurial-scm.org/D688: This now > seems unnecessary since the first thing you do is to convert it back into a > very similar tuple. Do you think the is still useful? Maybe use a named tuple > if we want the names? Either way, that's not for this patch, of course. I've added a diff to the end of the stack to change it to a tuple. No need for names I think, since the return value is only consumed internal to the class. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D730 To: durham, indygreg, #hg-reviewers Cc: martinvonz, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D745: bundlerepo: update to use new deltaiter api
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D745 AFFECTED FILES mercurial/bundlerepo.py CHANGE DETAILS diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -55,25 +55,16 @@ self.bundle = bundle n = len(self) self.repotiprev = n - 1 -chain = None self.bundlerevs = set() # used by 'bundle()' revset expression -getchunk = lambda: bundle.deltachunk(chain) -for chunkdata in iter(getchunk, {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] -flags = chunkdata['flags'] +for deltadata in bundle.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata size = len(delta) start = bundle.tell() - size link = linkmapper(cs) if node in self.nodemap: # this can happen if two branches make the same change -chain = node self.bundlerevs.add(self.nodemap[node]) continue @@ -93,7 +84,6 @@ self.index.insert(-1, e) self.nodemap[node] = n self.bundlerevs.add(n) -chain = node n += 1 def _chunk(self, rev): To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D746: changegroup: remove dictionary creation from deltachunk
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Previously delta chunk returned a dictionary. Now that we consume deltachunk within changegroup (instead of outside in revlog) we can just return a tuple and have it be returned directly by deltaiter. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D746 AFFECTED FILES mercurial/changegroup.py CHANGE DETAILS diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -188,8 +188,7 @@ header = struct.unpack(self.deltaheader, headerdata) delta = readexactly(self._stream, l - self.deltaheadersize) node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode) -return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs, -'deltabase': deltabase, 'delta': delta, 'flags': flags} +return (node, p1, p2, cs, deltabase, delta, flags) def getchunks(self): """returns all the chunks contains in the bundle @@ -438,17 +437,9 @@ """ chain = None for chunkdata in iter(lambda: self.deltachunk(chain), {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] -flags = chunkdata['flags'] - -chain = node - -yield (node, p1, p2, cs, deltabase, delta, flags) +# Chunkdata: (node, p1, p2, cs, deltabase, delta, flags) +yield chunkdata +chain = chunkdata[0] class cg2unpacker(cg1unpacker): """Unpacker for cg2 streams. To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D744: debug: update debugbundle to use new deltaiter api
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Changegroup now has a deltaiter api for easy iteration over a series of deltas. Let's use that in the debugbundle command. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D744 AFFECTED FILES mercurial/debugcommands.py CHANGE DETAILS diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -263,18 +263,11 @@ def showchunks(named): ui.write("\n%s%s\n" % (indent_string, named)) -chain = None -for chunkdata in iter(lambda: gen.deltachunk(chain), {}): -node = chunkdata['node'] -p1 = chunkdata['p1'] -p2 = chunkdata['p2'] -cs = chunkdata['cs'] -deltabase = chunkdata['deltabase'] -delta = chunkdata['delta'] +for deltadata in gen.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata ui.write("%s%s %s %s %s %s %s\n" % (indent_string, hex(node), hex(p1), hex(p2), hex(cs), hex(deltabase), len(delta))) -chain = node chunkdata = gen.changelogheader() showchunks("changelog") @@ -287,11 +280,9 @@ if isinstance(gen, bundle2.unbundle20): raise error.Abort(_('use debugbundle2 for this file')) chunkdata = gen.changelogheader() -chain = None -for chunkdata in iter(lambda: gen.deltachunk(chain), {}): -node = chunkdata['node'] +for deltadata in gen.deltaiter(): +node, p1, p2, cs, deltabase, delta, flags = deltadata ui.write("%s%s\n" % (indent_string, hex(node))) -chain = node def _debugobsmarkers(ui, part, indent=0, **opts): """display version and markers contained in 'data'""" To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D708: bundle2: remove unnecessary try finally
This revision was automatically updated to reflect the committed changes. Closed by commit rHGcc7b37c90616: bundle2: remove unnecessary try finally (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D708?vs=1820=1875 REVISION DETAIL https://phab.mercurial-scm.org/D708 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -505,32 +505,28 @@ The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" -try: -handler = _gethandler(op, part) -if handler is None: -return +handler = _gethandler(op, part) +if handler is None: +return -# handler is called outside the above try block so that we don't -# risk catching KeyErrors from anything other than the -# parthandlermapping lookup (any KeyError raised by handler() -# itself represents a defect of a different variety). -output = None -if op.captureoutput and op.reply is not None: -op.ui.pushbuffer(error=True, subproc=True) -output = '' -try: -handler(op, part) -finally: -if output is not None: -output = op.ui.popbuffer() -if output: -outpart = op.reply.newpart('output', data=output, - mandatory=False) -outpart.addparam( -'in-reply-to', pycompat.bytestr(part.id), mandatory=False) +# handler is called outside the above try block so that we don't +# risk catching KeyErrors from anything other than the +# parthandlermapping lookup (any KeyError raised by handler() +# itself represents a defect of a different variety). +output = None +if op.captureoutput and op.reply is not None: +op.ui.pushbuffer(error=True, subproc=True) +output = '' +try: +handler(op, part) finally: -pass - +if output is not None: +output = op.ui.popbuffer() +if output: +outpart = op.reply.newpart('output', data=output, + mandatory=False) +outpart.addparam( +'in-reply-to', pycompat.bytestr(part.id), mandatory=False) def decodecaps(blob): """decode a bundle2 caps bytes blob into a dictionary To: durham, #hg-reviewers, indygreg, krbullock Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D709: bundle2: move part processing to a separate function
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf010097c885c: bundle2: move part processing to a separate function (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D709?vs=1821=1876 REVISION DETAIL https://phab.mercurial-scm.org/D709 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -447,12 +447,15 @@ msg.append('\n') repo.ui.debug(''.join(msg)) +processparts(repo, op, unbundler) + +return op + +def processparts(repo, op, unbundler): with partiterator(repo, op, unbundler) as parts: for part in parts: _processpart(op, part) -return op - def _processchangegroup(op, cg, tr, source, url, **kwargs): ret = cg.apply(op.repo, tr, source, url, **kwargs) op.records.add('changegroup', { To: durham, #hg-reviewers, krbullock Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D707: bundle2: move handler validation out of processpart
This revision was automatically updated to reflect the committed changes. Closed by commit rHG07e4170f02f3: bundle2: move handler validation out of processpart (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D707?vs=1819=1873 REVISION DETAIL https://phab.mercurial-scm.org/D707 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -460,48 +460,55 @@ }) return ret +def _gethandler(op, part): +status = 'unknown' # used by debug output +try: +handler = parthandlermapping.get(part.type) +if handler is None: +status = 'unsupported-type' +raise error.BundleUnknownFeatureError(parttype=part.type) +indebug(op.ui, 'found a handler for part %r' % part.type) +unknownparams = part.mandatorykeys - handler.params +if unknownparams: +unknownparams = list(unknownparams) +unknownparams.sort() +status = 'unsupported-params (%s)' % unknownparams +raise error.BundleUnknownFeatureError(parttype=part.type, + params=unknownparams) +status = 'supported' +except error.BundleUnknownFeatureError as exc: +if part.mandatory: # mandatory parts +raise +indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) +return # skip to part processing +finally: +if op.ui.debugflag: +msg = ['bundle2-input-part: "%s"' % part.type] +if not part.mandatory: +msg.append(' (advisory)') +nbmp = len(part.mandatorykeys) +nbap = len(part.params) - nbmp +if nbmp or nbap: +msg.append(' (params:') +if nbmp: +msg.append(' %i mandatory' % nbmp) +if nbap: +msg.append(' %i advisory' % nbmp) +msg.append(')') +msg.append(' %s\n' % status) +op.ui.debug(''.join(msg)) + +return handler + def _processpart(op, part): """process a single part from a bundle The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" -status = 'unknown' # used by debug output try: -try: -handler = parthandlermapping.get(part.type) -if handler is None: -status = 'unsupported-type' -raise error.BundleUnknownFeatureError(parttype=part.type) -indebug(op.ui, 'found a handler for part %r' % part.type) -unknownparams = part.mandatorykeys - handler.params -if unknownparams: -unknownparams = list(unknownparams) -unknownparams.sort() -status = 'unsupported-params (%s)' % unknownparams -raise error.BundleUnknownFeatureError(parttype=part.type, - params=unknownparams) -status = 'supported' -except error.BundleUnknownFeatureError as exc: -if part.mandatory: # mandatory parts -raise -indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) -return # skip to part processing -finally: -if op.ui.debugflag: -msg = ['bundle2-input-part: "%s"' % part.type] -if not part.mandatory: -msg.append(' (advisory)') -nbmp = len(part.mandatorykeys) -nbap = len(part.params) - nbmp -if nbmp or nbap: -msg.append(' (params:') -if nbmp: -msg.append(' %i mandatory' % nbmp) -if nbap: -msg.append(' %i advisory' % nbmp) -msg.append(')') -msg.append(' %s\n' % status) -op.ui.debug(''.join(msg)) +handler = _gethandler(op, part) +if handler is None: +return # handler is called outside the above try block so that we don't # risk catching KeyErrors from anything other than the To: durham, #hg-reviewers, indygreg Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D477: revlog: add option to mmap revlog index
durham added a comment. Also, the improvements should become more pronounced if we fix some algorithms (like phases) that access really old commits. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D477 To: mbthomas, #fbhgext, indygreg, #hg-reviewers, durin42, quark Cc: durham, quark, durin42, simonfar, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D696: registrar: add a enum 'cmdtype' for the type of the command
durham accepted this revision. durham added a comment. I think the name could be better, but that can be bikeshed. Stamping my approval for the concept and pattern. INLINE COMMENTS > registrar.py:148 > > +class cmdtype(object): > +""" enum for the type of command which will tell whether the command is `cmdtype` might be overly vague, since I could imagine a number of classifications it could mean. Maybe "cmdwritetype"? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D696 To: pulkit, #hg-reviewers, durham Cc: durham, indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D706: bundle2: move processpart stream maintenance into part iterator
durham updated this revision to Diff 1818. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D706?vs=1802=1818 REVISION DETAIL https://phab.mercurial-scm.org/D706 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -354,21 +354,32 @@ self.unbundler = unbundler self.iterator = None self.count = 0 +self.current = None def __enter__(self): def func(): itr = enumerate(self.unbundler.iterparts()) for count, p in itr: self.count = count +self.current = p yield p +p.seek(0, 2) +self.current = None self.iterator = func() return self.iterator def __exit__(self, type, exc, tb): if not self.iterator: return if exc: +# If exiting or interrupted, do not attempt to seek the stream in +# the finally block below. This makes abort faster. +if (self.current and +not isinstance(exc, (SystemExit, KeyboardInterrupt))): +# consume the part content to not corrupt the stream. +self.current.seek(0, 2) + # Any exceptions seeking to the end of the bundle at this point are # almost certainly related to the underlying stream being bad. # And, chances are that the exception we're handling is related to @@ -455,7 +466,6 @@ The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" status = 'unknown' # used by debug output -hardabort = False try: try: handler = parthandlermapping.get(part.type) @@ -511,15 +521,8 @@ mandatory=False) outpart.addparam( 'in-reply-to', pycompat.bytestr(part.id), mandatory=False) -# If exiting or interrupted, do not attempt to seek the stream in the -# finally block below. This makes abort faster. -except (SystemExit, KeyboardInterrupt): -hardabort = True -raise finally: -# consume the part content to not corrupt the stream. -if not hardabort: -part.seek(0, 2) +pass def decodecaps(blob): @@ -1143,7 +1146,15 @@ return part = unbundlepart(self.ui, headerblock, self._fp) op = interruptoperation(self.ui) -_processpart(op, part) +hardabort = False +try: +_processpart(op, part) +except (SystemExit, KeyboardInterrupt): +hardabort = True +raise +finally: +if not hardabort: +part.seek(0, 2) self.ui.debug('bundle2-input-stream-interrupt:' ' closing out of band context\n') To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D707: bundle2: move handler validation out of processpart
durham updated this revision to Diff 1819. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D707?vs=1803=1819 REVISION DETAIL https://phab.mercurial-scm.org/D707 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -460,48 +460,55 @@ }) return ret +def _gethandler(op, part): +status = 'unknown' # used by debug output +try: +handler = parthandlermapping.get(part.type) +if handler is None: +status = 'unsupported-type' +raise error.BundleUnknownFeatureError(parttype=part.type) +indebug(op.ui, 'found a handler for part %r' % part.type) +unknownparams = part.mandatorykeys - handler.params +if unknownparams: +unknownparams = list(unknownparams) +unknownparams.sort() +status = 'unsupported-params (%s)' % unknownparams +raise error.BundleUnknownFeatureError(parttype=part.type, + params=unknownparams) +status = 'supported' +except error.BundleUnknownFeatureError as exc: +if part.mandatory: # mandatory parts +raise +indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) +return # skip to part processing +finally: +if op.ui.debugflag: +msg = ['bundle2-input-part: "%s"' % part.type] +if not part.mandatory: +msg.append(' (advisory)') +nbmp = len(part.mandatorykeys) +nbap = len(part.params) - nbmp +if nbmp or nbap: +msg.append(' (params:') +if nbmp: +msg.append(' %i mandatory' % nbmp) +if nbap: +msg.append(' %i advisory' % nbmp) +msg.append(')') +msg.append(' %s\n' % status) +op.ui.debug(''.join(msg)) + +return handler + def _processpart(op, part): """process a single part from a bundle The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" -status = 'unknown' # used by debug output try: -try: -handler = parthandlermapping.get(part.type) -if handler is None: -status = 'unsupported-type' -raise error.BundleUnknownFeatureError(parttype=part.type) -indebug(op.ui, 'found a handler for part %r' % part.type) -unknownparams = part.mandatorykeys - handler.params -if unknownparams: -unknownparams = list(unknownparams) -unknownparams.sort() -status = 'unsupported-params (%s)' % unknownparams -raise error.BundleUnknownFeatureError(parttype=part.type, - params=unknownparams) -status = 'supported' -except error.BundleUnknownFeatureError as exc: -if part.mandatory: # mandatory parts -raise -indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) -return # skip to part processing -finally: -if op.ui.debugflag: -msg = ['bundle2-input-part: "%s"' % part.type] -if not part.mandatory: -msg.append(' (advisory)') -nbmp = len(part.mandatorykeys) -nbap = len(part.params) - nbmp -if nbmp or nbap: -msg.append(' (params:') -if nbmp: -msg.append(' %i mandatory' % nbmp) -if nbap: -msg.append(' %i advisory' % nbmp) -msg.append(')') -msg.append(' %s\n' % status) -op.ui.debug(''.join(msg)) +handler = _gethandler(op, part) +if handler is None: +return # handler is called outside the above try block so that we don't # risk catching KeyErrors from anything other than the To: durham, #hg-reviewers, indygreg Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D708: bundle2: remove unnecessary try finally
durham updated this revision to Diff 1820. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D708?vs=1804=1820 REVISION DETAIL https://phab.mercurial-scm.org/D708 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -505,32 +505,28 @@ The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" -try: -handler = _gethandler(op, part) -if handler is None: -return +handler = _gethandler(op, part) +if handler is None: +return -# handler is called outside the above try block so that we don't -# risk catching KeyErrors from anything other than the -# parthandlermapping lookup (any KeyError raised by handler() -# itself represents a defect of a different variety). -output = None -if op.captureoutput and op.reply is not None: -op.ui.pushbuffer(error=True, subproc=True) -output = '' -try: -handler(op, part) -finally: -if output is not None: -output = op.ui.popbuffer() -if output: -outpart = op.reply.newpart('output', data=output, - mandatory=False) -outpart.addparam( -'in-reply-to', pycompat.bytestr(part.id), mandatory=False) +# handler is called outside the above try block so that we don't +# risk catching KeyErrors from anything other than the +# parthandlermapping lookup (any KeyError raised by handler() +# itself represents a defect of a different variety). +output = None +if op.captureoutput and op.reply is not None: +op.ui.pushbuffer(error=True, subproc=True) +output = '' +try: +handler(op, part) finally: -pass - +if output is not None: +output = op.ui.popbuffer() +if output: +outpart = op.reply.newpart('output', data=output, + mandatory=False) +outpart.addparam( +'in-reply-to', pycompat.bytestr(part.id), mandatory=False) def decodecaps(blob): """decode a bundle2 caps bytes blob into a dictionary To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D705: bundle2: move exception handling into part iterator
This revision was automatically updated to reflect the committed changes. Closed by commit rHG3d8fb0c37e12: bundle2: move exception handling into part iterator (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D705?vs=1801=1809 REVISION DETAIL https://phab.mercurial-scm.org/D705 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -348,8 +348,9 @@ return op class partiterator(object): -def __init__(self, repo, unbundler): +def __init__(self, repo, op, unbundler): self.repo = repo +self.op = op self.unbundler = unbundler self.iterator = None self.count = 0 @@ -363,10 +364,43 @@ self.iterator = func() return self.iterator -def __exit__(self, type, value, tb): +def __exit__(self, type, exc, tb): if not self.iterator: return +if exc: +# Any exceptions seeking to the end of the bundle at this point are +# almost certainly related to the underlying stream being bad. +# And, chances are that the exception we're handling is related to +# getting in that bad state. So, we swallow the seeking error and +# re-raise the original error. +seekerror = False +try: +for part in self.iterator: +# consume the bundle content +part.seek(0, 2) +except Exception: +seekerror = True + +# Small hack to let caller code distinguish exceptions from bundle2 +# processing from processing the old format. This is mostly needed +# to handle different return codes to unbundle according tothe type +# of bundle. We should probably clean up or drop this return code +# craziness in a future version. +exc.duringunbundle2 = True +salvaged = [] +replycaps = None +if self.op.reply is not None: +salvaged = self.op.reply.salvageoutput() +replycaps = self.op.reply.capabilities +exc._replycaps = replycaps +exc._bundle2salvagedoutput = salvaged + +# Re-raising from a variable loses the original stack. So only use +# that form if we need to. +if seekerror: +raise exc + self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' % self.count) @@ -402,45 +436,9 @@ msg.append('\n') repo.ui.debug(''.join(msg)) -with partiterator(repo, unbundler) as parts: -part = None -try: -for part in parts: -_processpart(op, part) -except Exception as exc: -# Any exceptions seeking to the end of the bundle at this point are -# almost certainly related to the underlying stream being bad. -# And, chances are that the exception we're handling is related to -# getting in that bad state. So, we swallow the seeking error and -# re-raise the original error. -seekerror = False -try: -for part in parts: -# consume the bundle content -part.seek(0, 2) -except Exception: -seekerror = True - -# Small hack to let caller code distinguish exceptions from bundle2 -# processing from processing the old format. This is mostly needed -# to handle different return codes to unbundle according to the type -# of bundle. We should probably clean up or drop this return code -# craziness in a future version. -exc.duringunbundle2 = True -salvaged = [] -replycaps = None -if op.reply is not None: -salvaged = op.reply.salvageoutput() -replycaps = op.reply.capabilities -exc._replycaps = replycaps -exc._bundle2salvagedoutput = salvaged - -# Re-raising from a variable loses the original stack. So only use -# that form if we need to. -if seekerror: -raise exc -else: -raise +with partiterator(repo, op, unbundler) as parts: +for part in parts: +_processpart(op, part) return op To: durham, #hg-reviewers, indygreg Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D704: bundle2: move part counter to partiterator
This revision was automatically updated to reflect the committed changes. Closed by commit rHG550343626bb2: bundle2: move part counter to partiterator (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D704?vs=1800=1808 REVISION DETAIL https://phab.mercurial-scm.org/D704 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -348,14 +348,27 @@ return op class partiterator(object): -def __init__(self, unbundler): +def __init__(self, repo, unbundler): +self.repo = repo self.unbundler = unbundler +self.iterator = None +self.count = 0 def __enter__(self): -return enumerate(self.unbundler.iterparts()) +def func(): +itr = enumerate(self.unbundler.iterparts()) +for count, p in itr: +self.count = count +yield p +self.iterator = func() +return self.iterator def __exit__(self, type, value, tb): -pass +if not self.iterator: +return + +self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' % + self.count) def processbundle(repo, unbundler, transactiongetter=None, op=None): """This function process a bundle, apply effect to/from a repo @@ -389,11 +402,10 @@ msg.append('\n') repo.ui.debug(''.join(msg)) -with partiterator(unbundler) as parts: +with partiterator(repo, unbundler) as parts: part = None -nbpart = 0 try: -for nbpart, part in parts: +for part in parts: _processpart(op, part) except Exception as exc: # Any exceptions seeking to the end of the bundle at this point are @@ -403,7 +415,7 @@ # re-raise the original error. seekerror = False try: -for nbpart, part in parts: +for part in parts: # consume the bundle content part.seek(0, 2) except Exception: @@ -429,8 +441,6 @@ raise exc else: raise -finally: -repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart) return op To: durham, #hg-reviewers, indygreg Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D703: bundle2: move part iterator a separate class
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe9e0e1143fc5: bundle2: move part iterator a separate class (authored by durham, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D703?vs=1799=1807 REVISION DETAIL https://phab.mercurial-scm.org/D703 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -347,6 +347,16 @@ _processchangegroup(op, unbundler, tr, source, url, **kwargs) return op +class partiterator(object): +def __init__(self, unbundler): +self.unbundler = unbundler + +def __enter__(self): +return enumerate(self.unbundler.iterparts()) + +def __exit__(self, type, value, tb): +pass + def processbundle(repo, unbundler, transactiongetter=None, op=None): """This function process a bundle, apply effect to/from a repo @@ -378,48 +388,49 @@ msg.append(' with-transaction') msg.append('\n') repo.ui.debug(''.join(msg)) -iterparts = enumerate(unbundler.iterparts()) -part = None -nbpart = 0 -try: -for nbpart, part in iterparts: -_processpart(op, part) -except Exception as exc: -# Any exceptions seeking to the end of the bundle at this point are -# almost certainly related to the underlying stream being bad. -# And, chances are that the exception we're handling is related to -# getting in that bad state. So, we swallow the seeking error and -# re-raise the original error. -seekerror = False + +with partiterator(unbundler) as parts: +part = None +nbpart = 0 try: -for nbpart, part in iterparts: -# consume the bundle content -part.seek(0, 2) -except Exception: -seekerror = True +for nbpart, part in parts: +_processpart(op, part) +except Exception as exc: +# Any exceptions seeking to the end of the bundle at this point are +# almost certainly related to the underlying stream being bad. +# And, chances are that the exception we're handling is related to +# getting in that bad state. So, we swallow the seeking error and +# re-raise the original error. +seekerror = False +try: +for nbpart, part in parts: +# consume the bundle content +part.seek(0, 2) +except Exception: +seekerror = True -# Small hack to let caller code distinguish exceptions from bundle2 -# processing from processing the old format. This is mostly -# needed to handle different return codes to unbundle according to the -# type of bundle. We should probably clean up or drop this return code -# craziness in a future version. -exc.duringunbundle2 = True -salvaged = [] -replycaps = None -if op.reply is not None: -salvaged = op.reply.salvageoutput() -replycaps = op.reply.capabilities -exc._replycaps = replycaps -exc._bundle2salvagedoutput = salvaged +# Small hack to let caller code distinguish exceptions from bundle2 +# processing from processing the old format. This is mostly needed +# to handle different return codes to unbundle according to the type +# of bundle. We should probably clean up or drop this return code +# craziness in a future version. +exc.duringunbundle2 = True +salvaged = [] +replycaps = None +if op.reply is not None: +salvaged = op.reply.salvageoutput() +replycaps = op.reply.capabilities +exc._replycaps = replycaps +exc._bundle2salvagedoutput = salvaged -# Re-raising from a variable loses the original stack. So only use -# that form if we need to. -if seekerror: -raise exc -else: -raise -finally: -repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart) +# Re-raising from a variable loses the original stack. So only use +# that form if we need to. +if seekerror: +raise exc +else: +raise +finally: +repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart) return op To: durham, #hg-reviewers, indygreg Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D706: bundle2: move processpart stream maintenance into part iterator
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The processpart function also did some stream maintenance, so let's move it to the part iterator as well, as part of moving all part iteration logic into the class. There is one place processpart is called outside of the normal loop, so we manually handle the seek there. The now-empty try/finally will be removed in a later patch, for ease of review. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D706 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -354,21 +354,32 @@ self.unbundler = unbundler self.iterator = None self.count = 0 +self.current = None def __enter__(self): def func(): itr = enumerate(self.unbundler.iterparts()) for count, p in itr: self.count = count +self.current = p yield p +p.seek(0, 2) +self.current = None self.iterator = func() return self.iterator def __exit__(self, type, exc, tb): if not self.iterator: return if exc: +# If exiting or interrupted, do not attempt to seek the stream in +# the finally block below. This makes abort faster. +if (self.current and +not isinstance(exc, (SystemExit, KeyboardInterrupt))): +# consume the part content to not corrupt the stream. +self.current.seek(0, 2) + # Any exceptions seeking to the end of the bundle at this point are # almost certainly related to the underlying stream being bad. # And, chances are that the exception we're handling is related to @@ -455,7 +466,6 @@ The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" status = 'unknown' # used by debug output -hardabort = False try: try: handler = parthandlermapping.get(part.type) @@ -511,15 +521,8 @@ mandatory=False) outpart.addparam( 'in-reply-to', pycompat.bytestr(part.id), mandatory=False) -# If exiting or interrupted, do not attempt to seek the stream in the -# finally block below. This makes abort faster. -except (SystemExit, KeyboardInterrupt): -hardabort = True -raise finally: -# consume the part content to not corrupt the stream. -if not hardabort: -part.seek(0, 2) +pass def decodecaps(blob): @@ -1143,7 +1146,10 @@ return part = unbundlepart(self.ui, headerblock, self._fp) op = interruptoperation(self.ui) -_processpart(op, part) +try: +_processpart(op, part) +finally: +part.seek(0, 2) self.ui.debug('bundle2-input-stream-interrupt:' ' closing out of band context\n') To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D708: bundle2: remove unnecessary try finally
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This is no longer needed. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D708 AFFECTED FILES mercurial/bundle2.py CHANGE DETAILS diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -505,32 +505,28 @@ The part is guaranteed to have been fully consumed when the function exits (even if an exception is raised).""" -try: -handler = _gethandler(op, part) -if handler is None: -return +handler = _gethandler(op, part) +if handler is None: +return -# handler is called outside the above try block so that we don't -# risk catching KeyErrors from anything other than the -# parthandlermapping lookup (any KeyError raised by handler() -# itself represents a defect of a different variety). -output = None -if op.captureoutput and op.reply is not None: -op.ui.pushbuffer(error=True, subproc=True) -output = '' -try: -handler(op, part) -finally: -if output is not None: -output = op.ui.popbuffer() -if output: -outpart = op.reply.newpart('output', data=output, - mandatory=False) -outpart.addparam( -'in-reply-to', pycompat.bytestr(part.id), mandatory=False) +# handler is called outside the above try block so that we don't +# risk catching KeyErrors from anything other than the +# parthandlermapping lookup (any KeyError raised by handler() +# itself represents a defect of a different variety). +output = None +if op.captureoutput and op.reply is not None: +op.ui.pushbuffer(error=True, subproc=True) +output = '' +try: +handler(op, part) finally: -pass - +if output is not None: +output = op.ui.popbuffer() +if output: +outpart = op.reply.newpart('output', data=output, + mandatory=False) +outpart.addparam( +'in-reply-to', pycompat.bytestr(part.id), mandatory=False) def decodecaps(blob): """decode a bundle2 caps bytes blob into a dictionary To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel