D4820: logtoprocess: sends the canonical command name to the subprocess
lothiraldan added a comment. In https://phab.mercurial-scm.org/D4820#73256, @yuja wrote: > > +def extsetup(): > > +# this is to get the canonical name of the command: "commit", not "ci" > > +def wrapdispatch(orig, *args, **kwargs): > > +encoding.environ.pop("LTP_COMMAND", None) > > +return orig(*args, **kwargs) > > + > > +def wrapruncommand(orig, *args, **kwargs): > > +encoding.environ["LTP_COMMAND"] = args[2] > > +return orig(*args, **kwargs) > > + > > +extensions.wrapfunction(dispatch,'dispatch',wrapdispatch) > > +extensions.wrapfunction(dispatch,'runcommand',wrapruncommand) > > It's scary to update the environment variables globally and pass > `LTP_COMMAND` in to any child processes. > > Can you add a proper way to teach the command name to the logtoprocess > extension, by `ui.log()` for example? > > Also, the word `LTP` isn't used anywhere. It'll need a better name, and > should be documented. I tried to pass it to the `ui.log()` but didn't find a clean way to transfer it from `dispatch._dispatch` to `dispatch.dispatch`. I tried storing it in the ui object but without luck. Do you think it would be ok to store it in the `request` object? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4820 To: lothiraldan, #hg-reviewers Cc: yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH] lfs: register the flag processors per repository
# HG changeset patch # User Matt Harbison # Date 1538626646 14400 # Thu Oct 04 00:17:26 2018 -0400 # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31 # Parent 7a347d362a455d84bccf34347171d89724b9c9df lfs: register the flag processors per repository Previously, enabling the extension for any repo in commandserver or hgweb would enable the flags on all repos. Since localrepo.resolverevlogstorevfsoptions() is called so early, the svfs option needs to be forced on later in reposetup() to handle the case where the extension loads, but no lfs requirement has been set. That way, the flags processor is enabled when committing or unbundling. This will also ensure that the flags processor is available on all revlogs in a long running process. I'm not sure if we need a formal API to add options like this- narrow doesn't seem to need it. I'm also not sure if REPO_FEATURE_LFS should just be added unconditionally in reposetup()- it's easy enough to check the requirements to see if a blob was actually committed. diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py --- a/hgext/lfs/__init__.py +++ b/hgext/lfs/__init__.py @@ -218,6 +218,7 @@ def reposetup(ui, repo): repo.svfs.lfslocalblobstore = blobstore.local(repo) repo.svfs.lfsremoteblobstore = blobstore.remote(repo) +repo.svfs.options[b'enableextstored'] = True class lfsrepo(repo.__class__): @localrepo.unfilteredmethod @@ -334,14 +335,9 @@ def extsetup(ui): wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary) context.basefilectx.islfs = wrapper.filectxislfs -revlog.addflagprocessor( -revlog.REVIDX_EXTSTORED, -( -wrapper.readfromstore, -wrapper.writetostore, -wrapper.bypasscheckhash, -), -) +wrapfunction(revlog, 'extstoredrawprocessor', wrapper.bypasscheckhash) +wrapfunction(revlog, 'extstoredreadprocessor', wrapper.readfromstore) +wrapfunction(revlog, 'extstoredwriteprocessor', wrapper.writetostore) scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles) diff --git a/hgext/lfs/wrapper.py b/hgext/lfs/wrapper.py --- a/hgext/lfs/wrapper.py +++ b/hgext/lfs/wrapper.py @@ -50,10 +50,10 @@ def _capabilities(orig, repo, proto): caps.append('lfs') return caps -def bypasscheckhash(self, text): +def bypasscheckhash(orig, self, text): return False -def readfromstore(self, text): +def readfromstore(orig, self, text): """Read filelog content from local blobstore transform for flagprocessor. Default tranform for flagprocessor, returning contents from blobstore. @@ -81,7 +81,7 @@ def readfromstore(self, text): return (text, True) -def writetostore(self, text): +def writetostore(orig, self, text): # hg filelog metadata (includes rename, etc) hgmeta, offset = storageutil.parsemeta(text) if offset and offset > 0: diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -763,6 +763,9 @@ def resolverevlogstorevfsoptions(ui, req if r.startswith(b'exp-compression-'): options[b'compengine'] = r[len(b'exp-compression-'):] +if b'lfs' in requirements: +options[b'enableextstored'] = True + if repository.NARROW_REQUIREMENT in requirements: options[b'enableellipsis'] = True diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers') REVIDX_ISCENSORED: None, } +# Flag processors for REVIDX_EXTSTORED. +def extstoredreadprocessor(rl, text): +raise error.ProgrammingError(b'extstored flags processor enabled without' + b' wrapping processor function') + +def extstoredwriteprocessor(rl, text): +raise error.ProgrammingError(b'extstored flags processor enabled without' + b' wrapping processor function') + +def extstoredrawprocessor(rl, text): +raise error.ProgrammingError(b'extstored flags processor enabled without' + b' wrapping processor function') + +# Lambdas are needed here so that these methods can be wrapped by lfs. +extstoredprocessor = ( +lambda rl, text: extstoredreadprocessor(rl, text), +lambda rl, text: extstoredwriteprocessor(rl, text), +lambda rl, text: extstoredrawprocessor(rl, text), +) + # Flag processors for REVIDX_ELLIPSIS. def ellipsisreadprocessor(rl, text): return text, False @@ -405,6 +425,8 @@ class revlog(object): self._srdensitythreshold = opts['sparse-read-density-threshold'] if 'sparse-read-min-gap-size' in opts: self._srmingapsize = opts['sparse-read-min-gap-size'] +if opts.get('enableextstored'): +self._flagprocessors[REVIDX_EXTSTORED] = extstoredprocessor if opts.get('enableellipsis'):
D4713: largefiles: automatically load largefiles extension when required (BC)
mharbison72 added a comment. In https://phab.mercurial-scm.org/D4713#73260, @yuja wrote: > IIUC, we would have to enable the largefiles extension once to clone the > repo, but that's no longer needed. So you can enable the extension without > hearing its name at all. > > Perhaps, a warning can be displayed when the non-core repository requirement > is added? I'll take a look and see what I can figure out, but it likely won't be until next week. I think it should maybe be limited to a clone/unbundle adding a requirement with an implicitly loaded extension. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4713 To: indygreg, #hg-reviewers Cc: mharbison72, yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4860: repository: define and use revision flag constants
martinvonz added inline comments. INLINE COMMENTS > changegroups.txt:139-142 > +8192 > + Externally stored. The revision fulltext contains ``key:value`` ``\n`` > + delimited metadata defining an object stored elsewhere. Used by the LFS > + extension. Add a TODO about removing this flag from the changegroup? Can be done in a followup. I'm also not sure if this is the right place for a TODO. Maybe it should be in repository.py? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4860 To: 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
D4858: httppeer: report http statistics
This revision was automatically updated to reflect the committed changes. Closed by commit rHG393e44324037: httppeer: report http statistics (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4858?vs=11636=11665 REVISION DETAIL https://phab.mercurial-scm.org/D4858 AFFECTED FILES mercurial/httppeer.py mercurial/url.py tests/test-clone-uncompressed.t tests/test-clonebundles.t tests/test-http-api-httpv2.t tests/test-http-protocol.t tests/test-lfs-serve-access.t tests/test-lfs-serve.t tests/test-schemes.t tests/test-stream-bundle-v2.t tests/test-wireproto-caching.t tests/test-wireproto-command-branchmap.t tests/test-wireproto-command-capabilities.t tests/test-wireproto-command-changesetdata.t tests/test-wireproto-command-filedata.t tests/test-wireproto-command-heads.t tests/test-wireproto-command-known.t tests/test-wireproto-command-listkeys.t tests/test-wireproto-command-lookup.t tests/test-wireproto-command-manifestdata.t tests/test-wireproto-command-pushkey.t tests/test-wireproto-content-redirects.t tests/test-wireproto-exchangev2.t CHANGE DETAILS diff --git a/tests/test-wireproto-exchangev2.t b/tests/test-wireproto-exchangev2.t --- a/tests/test-wireproto-exchangev2.t +++ b/tests/test-wireproto-exchangev2.t @@ -130,6 +130,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:caa2a465451d (3 drafts) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) All changesets should have been transferred @@ -256,6 +257,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:4432d83626e8 + (sent 6 HTTP requests and * bytes; received * bytes in responses) (glob) $ cd client-singlehead @@ -369,6 +371,7 @@ updating the branch cache new changesets cd2534766bec:caa2a465451d (3 drafts) (run 'hg update' to get a working copy) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg log -G -T '{rev} {node} {phase}\n' o 4 caa2a465451dd1facda0f5b12312c355584188a1 draft @@ -439,6 +442,7 @@ checking for updated bookmarks 2 local changesets published (run 'hg update' to get a working copy) + (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg log -G -T '{rev} {node} {phase}\n' o 4 caa2a465451dd1facda0f5b12312c355584188a1 public @@ -555,6 +559,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:caa2a465451d (1 drafts) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg -R client-bookmarks bookmarks book-10:3390ef850073 @@ -611,6 +616,7 @@ checking for updated bookmarks updating bookmark book-1 (run 'hg update' to get a working copy) + (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg -R client-bookmarks bookmarks book-12:cd2534766bec diff --git a/tests/test-wireproto-content-redirects.t b/tests/test-wireproto-content-redirects.t --- a/tests/test-wireproto-content-redirects.t +++ b/tests/test-wireproto-content-redirects.t @@ -317,6 +317,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Unknown protocol is filtered from compatible targets @@ -610,6 +611,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Missing SNI support filters targets that require SNI @@ -901,6 +903,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) $ cat >> $HGRCPATH << EOF > [extensions] @@ -1191,6 +1194,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Set up the server to issue content redirects to its built-in API server. @@ -1279,6 +1283,7 @@ ] } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Cached entry should be available on server @@ -1381,6 +1386,7 @@ ] } ] + (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob) $ cat error.log $ killdaemons.py diff --git a/tests/test-wireproto-command-pushkey.t b/tests/test-wireproto-command-pushkey.t --- a/tests/test-wireproto-command-pushkey.t +++ b/tests/test-wireproto-command-pushkey.t @@ -63,6 +63,7 @@ s> \r\n received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos) response: True + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) $ sendhttpv2peer << EOF > command listkeys @@ -105,5 +106,6 @@ response: { b'@':
D4859: exchangev2: add progress bar around manifest scanning
This revision was automatically updated to reflect the committed changes. Closed by commit rHG7a347d362a45: exchangev2: add progress bar around manifest scanning (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4859?vs=11655=11666 REVISION DETAIL https://phab.mercurial-scm.org/D4859 AFFECTED FILES mercurial/exchangev2.py CHANGE DETAILS diff --git a/mercurial/exchangev2.py b/mercurial/exchangev2.py --- a/mercurial/exchangev2.py +++ b/mercurial/exchangev2.py @@ -320,19 +320,25 @@ ml = repo.manifestlog fnodes = collections.defaultdict(dict) -for manifestnode in manifestnodes: -m = ml.get(b'', manifestnode) +progress = repo.ui.makeprogress( +_('scanning manifests'), total=len(manifestnodes)) + +with progress: +for manifestnode in manifestnodes: +m = ml.get(b'', manifestnode) -# TODO this will pull in unwanted nodes because it takes the storage -# delta into consideration. What we really want is something that takes -# the delta between the manifest's parents. And ideally we would -# ignore file nodes that are known locally. For now, ignore both -# these limitations. This will result in incremental fetches requesting -# data we already have. So this is far from ideal. -md = m.readfast() +# TODO this will pull in unwanted nodes because it takes the storage +# delta into consideration. What we really want is something that +# takes the delta between the manifest's parents. And ideally we +# would ignore file nodes that are known locally. For now, ignore +# both these limitations. This will result in incremental fetches +# requesting data we already have. So this is far from ideal. +md = m.readfast() -for path, fnode in md.items(): -fnodes[path].setdefault(fnode, manifestnode) +for path, fnode in md.items(): +fnodes[path].setdefault(fnode, manifestnode) + +progress.increment() return fnodes To: 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
D4857: keepalive: track number of bytes received from an HTTP response
This revision was automatically updated to reflect the committed changes. Closed by commit rHG5e5b06087ec5: keepalive: track number of bytes received from an HTTP response (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4857?vs=11635=11664 REVISION DETAIL https://phab.mercurial-scm.org/D4857 AFFECTED FILES mercurial/keepalive.py CHANGE DETAILS diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -392,6 +392,7 @@ method=method, **extrakw) self.fileno = sock.fileno self.code = None +self.receivedbytescount = 0 self._rbuf = '' self._rbufsize = 8096 self._handler = None # inserted by the handler later @@ -436,7 +437,16 @@ # if it's not empty. s = self._rbuf self._rbuf = '' -s += self._raw_read(amt) +data = self._raw_read(amt) + +self.receivedbytescount += len(data) +self._connection.receivedbytescount += len(data) +try: +self._handler.parent.receivedbytescount += len(data) +except AttributeError: +pass + +s += data return s # stolen from Python SVN #68532 to fix issue1088 @@ -512,6 +522,13 @@ if not new: break +self.receivedbytescount += len(new) +self._connection.receivedbytescount += len(new) +try: +self._handler.parent.receivedbytescount += len(new) +except AttributeError: +pass + chunks.append(new) i = new.find('\n') if i >= 0: @@ -557,6 +574,14 @@ return total mv = memoryview(dest) got = self._raw_readinto(mv[have:total]) + +self.receivedbytescount += got +self._connection.receivedbytescount += got +try: +self._handler.receivedbytescount += got +except AttributeError: +pass + dest[0:have] = self._rbuf got += len(self._rbuf) self._rbuf = '' @@ -643,6 +668,7 @@ def __init__(self, *args, **kwargs): httplib.HTTPConnection.__init__(self, *args, **kwargs) self.sentbytescount = 0 +self.receivedbytescount = 0 # # TEST FUNCTIONS To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4856: keepalive: track request count and bytes sent
This revision was automatically updated to reflect the committed changes. Closed by commit rHGdc82ad1b7f77: keepalive: track request count and bytes sent (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4856?vs=11634=11663 REVISION DETAIL https://phab.mercurial-scm.org/D4856 AFFECTED FILES mercurial/keepalive.py CHANGE DETAILS diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -174,6 +174,8 @@ class KeepAliveHandler(object): def __init__(self): self._cm = ConnectionManager() +self.requestscount = 0 +self.sentbytescount = 0 Connection Management def open_connections(self): @@ -312,6 +314,8 @@ return r def _start_transaction(self, h, req): +oldbytescount = h.sentbytescount + # What follows mostly reimplements HTTPConnection.request() # except it adds self.parent.addheaders in the mix and sends headers # in a deterministic order (to make testing easier). @@ -346,6 +350,16 @@ if urllibcompat.hasdata(req): h.send(data) +# This will fail to record events in case of I/O failure. That's OK. +self.requestscount += 1 +self.sentbytescount += h.sentbytescount - oldbytescount + +try: +self.parent.requestscount += 1 +self.parent.sentbytescount += h.sentbytescount - oldbytescount +except AttributeError: +pass + class HTTPHandler(KeepAliveHandler, urlreq.httphandler): pass @@ -585,9 +599,11 @@ data = read(blocksize) while data: self.sock.sendall(data) +self.sentbytescount += len(data) data = read(blocksize) else: self.sock.sendall(str) +self.sentbytescount += len(str) except socket.error as v: reraise = True if v[0] == errno.EPIPE: # Broken pipe @@ -624,6 +640,9 @@ send = safesend getresponse = wrapgetresponse(httplib.HTTPConnection) +def __init__(self, *args, **kwargs): +httplib.HTTPConnection.__init__(self, *args, **kwargs) +self.sentbytescount = 0 # # TEST FUNCTIONS To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4855: url: have httpsconnection inherit from our custom HTTPConnection
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf2dffa1359c6: url: have httpsconnection inherit from our custom HTTPConnection (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4855?vs=11633=11662 REVISION DETAIL https://phab.mercurial-scm.org/D4855 AFFECTED FILES mercurial/keepalive.py mercurial/url.py CHANGE DETAILS diff --git a/mercurial/url.py b/mercurial/url.py --- a/mercurial/url.py +++ b/mercurial/url.py @@ -339,16 +339,16 @@ return logginghttpconnection(createconnection, *args, **kwargs) if has_https: -class httpsconnection(httplib.HTTPConnection): +class httpsconnection(keepalive.HTTPConnection): response_class = keepalive.HTTPResponse default_port = httplib.HTTPS_PORT # must be able to send big bundle as stream. send = _gen_sendfile(keepalive.safesend) getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection) def __init__(self, host, port=None, key_file=None, cert_file=None, *args, **kwargs): -httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs) +keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs) self.key_file = key_file self.cert_file = cert_file diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -615,6 +615,10 @@ return safegetresponse class HTTPConnection(httplib.HTTPConnection): +# url.httpsconnection inherits from this. So when adding/removing +# attributes, be sure to audit httpsconnection() for unintended +# consequences. + # use the modified response class response_class = HTTPResponse send = safesend To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4854: cborutil: change buffering strategy
This revision was automatically updated to reflect the committed changes. Closed by commit rHG62160d3077cd: cborutil: change buffering strategy (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4854?vs=11632=11661 REVISION DETAIL https://phab.mercurial-scm.org/D4854 AFFECTED FILES mercurial/utils/cborutil.py CHANGE DETAILS diff --git a/mercurial/utils/cborutil.py b/mercurial/utils/cborutil.py --- a/mercurial/utils/cborutil.py +++ b/mercurial/utils/cborutil.py @@ -913,7 +913,8 @@ """ def __init__(self): self._decoder = sansiodecoder() -self._leftover = None +self._chunks = [] +self._wanted = 0 def decode(self, b): """Attempt to decode bytes to CBOR values. @@ -924,19 +925,40 @@ * Integer number of bytes decoded from the new input. * Integer number of bytes wanted to decode the next value. """ +# Our strategy for buffering is to aggregate the incoming chunks in a +# list until we've received enough data to decode the next item. +# This is slightly more complicated than using an ``io.BytesIO`` +# or continuously concatenating incoming data. However, because it +# isn't constantly reallocating backing memory for a growing buffer, +# it prevents excessive memory thrashing and is significantly faster, +# especially in cases where the percentage of input chunks that don't +# decode into a full item is high. -if self._leftover: -oldlen = len(self._leftover) -b = self._leftover + b -self._leftover = None +if self._chunks: +# A previous call said we needed N bytes to decode the next item. +# But this call doesn't provide enough data. We buffer the incoming +# chunk without attempting to decode. +if len(b) < self._wanted: +self._chunks.append(b) +self._wanted -= len(b) +return False, 0, self._wanted + +# Else we may have enough data to decode the next item. Aggregate +# old data with new and reset the buffer. +newlen = len(b) +self._chunks.append(b) +b = b''.join(self._chunks) +self._chunks = [] +oldlen = len(b) - newlen + else: -b = b oldlen = 0 available, readcount, wanted = self._decoder.decode(b) +self._wanted = wanted if readcount < len(b): -self._leftover = b[readcount:] +self._chunks.append(b[readcount:]) return available, readcount - oldlen, wanted To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4876: amend: add config to skip amend if only date is changed (issue5828)
Zharaskhan created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY When --date flag specified with `hg amend` and working copy is clean, `hg amend` amends, which is generally undesirable behavior. But it is commonly used to change date on each amend. I added experimental.amend.dateonly config option to not amend if only thing that changed is date. I also added tests for this. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4876 AFFECTED FILES mercurial/cmdutil.py mercurial/configitems.py tests/test-amend.t CHANGE DETAILS diff --git a/tests/test-amend.t b/tests/test-amend.t --- a/tests/test-amend.t +++ b/tests/test-amend.t @@ -365,3 +365,40 @@ $ hg amend #endif + + $ hg status + $ hg diff + $ hg amend --date '1980-1-1 0:1' + $ hg export + # HG changeset patch + # User test + # Date 315532860 0 + # Tue Jan 01 00:01:00 1980 + + # Node ID 67c8ed970566d909b4759ad6db6048ea9080743b + # Parent cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + b + + diff --git a/b b/b + new file mode 100644 + --- /dev/null + +++ b/b + @@ -0,0 +1,1 @@ + +fixed + $ hg amend --date '1990-1-1 0:1' --config experimental.amend.dateonly=True + nothing changed + [1] + $ hg export + # HG changeset patch + # User test + # Date 315532860 0 + # Tue Jan 01 00:01:00 1980 + + # Node ID 67c8ed970566d909b4759ad6db6048ea9080743b + # Parent cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + b + + diff --git a/b b/b + new file mode 100644 + --- /dev/null + +++ b/b + @@ -0,0 +1,1 @@ + +fixed diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -437,6 +437,9 @@ coreconfigitem('email', 'to', default=None, ) +coreconfigitem('experimental', 'amend.dateonly', +default=False, +) coreconfigitem('experimental', 'archivemetatemplate', default=dynamicdefault, ) diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -2558,13 +2558,17 @@ if ((not changes) and newdesc == old.description() and user == old.user() -and date == old.date() and pureextra == old.extra()): -# nothing changed. continuing here would create a new node -# anyway because of the amend_source noise. -# -# This not what we expect from amend. -return old.node() + +# If the date is same or +# the user doesn't want to create a date only change amend +if date == old.date() or ui.configbool('experimental', + 'amend.dateonly'): +# nothing changed. continuing here would create a new node +# anyway because of the amend_source noise. +# +# This not what we expect from amend. +return old.node() commitphase = None if opts.get('secret'): To: Zharaskhan, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4875: treemanifests: remove _loadalllazy when doing copies
spectral created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY 'before' here is https://phab.mercurial-scm.org/D4845 (not the committed/rebased version) diff --git: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 1.329 s +- 0.011 s| 1.320 s +- 0.010 s | 99.3% m-u | | x | 1.316 s +- 0.005 s| 1.334 s +- 0.018 s | 101.4% m-u | x | | 1.330 s +- 0.021 s| 1.322 s +- 0.005 s | 99.4% m-u | x | x | 87.2 ms +- 0.7 ms| 86.9 ms +- 1.5 ms | 99.7% l-d-r | | | 203.3 ms +- 7.8 ms | 199.4 ms +- 1.8 ms | 98.1% l-d-r | | x | 204.6 ms +- 2.8 ms | 201.7 ms +- 2.1 ms | 98.6% l-d-r | x | | 90.5 ms +- 11.0 ms| 86.2 ms +- 1.0 ms | 95.2% l-d-r | x | x | 66.3 ms +- 2.0 ms| 66.4 ms +- 0.9 ms | 100.2% diff -c . --git: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 239.4 ms +- 2.0 ms | 241.7 ms +- 4.6 ms | 101.0% m-u | | x | 128.9 ms +- 1.9 ms | 130.9 ms +- 7.7 ms | 101.6% m-u | x | | 241.1 ms +- 1.6 ms | 240.1 ms +- 1.4 ms | 99.6% m-u | x | x | 133.4 ms +- 1.5 ms | 133.4 ms +- 1.2 ms | 100.0% l-d-r | | | 84.3 ms +- 1.5 ms| 83.5 ms +- 1.0 ms | 99.1% l-d-r | | x | 200.9 ms +- 6.3 ms | 203.0 ms +- 4.4 ms | 101.0% l-d-r | x | | 108.1 ms +- 1.4 ms | 108.7 ms +- 2.1 ms | 100.6% l-d-r | x | x | 190.2 ms +- 4.8 ms | 191.6 ms +- 2.0 ms | 100.7% rebase -r . --keep -d .^^: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 5.655 s +- 0.029 s| 5.640 s +- 0.036 s | 99.7% m-u | | x | 5.813 s +- 0.038 s| 5.773 s +- 0.028 s | 99.3% m-u | x | | 5.593 s +- 0.043 s| 5.589 s +- 0.028 s | 99.9% m-u | x | x | 648.2 ms +- 19.2 ms | 637.3 ms +- 27.7 ms | 98.3% l-d-r | | | 673.3 ms +- 8.0 ms | 673.2 ms +- 6.8 ms | 100.0% l-d-r | | x | 6.583 s +- 0.030 s| 5.721 s +- 0.028 s | 86.9% <-- l-d-r | x | | 277.8 ms +- 6.7 ms | 276.0 ms +- 2.7 ms | 99.4% l-d-r | x | x | 1.692 s +- 0.013 s| 720.9 ms +- 13.3 ms | 42.6% <-- status --change . --copies: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 220.9 ms +- 1.6 ms | 219.9 ms +- 2.2 ms | 99.5% m-u | | x | 109.2 ms +- 1.0 ms | 109.4 ms +- 0.8 ms | 100.2% m-u | x | | 222.6 ms +- 1.7 ms | 221.4 ms +- 2.1 ms | 99.5% m-u | x | x | 113.4 ms +- 0.5 ms | 113.1 ms +- 1.1 ms | 99.7% l-d-r | | | 82.1 ms +- 1.7 ms| 82.1 ms +- 1.2 ms | 100.0% l-d-r | | x | 199.8 ms +- 4.0 ms | 200.7 ms +- 3.6 ms | 100.5% l-d-r | x | | 85.4 ms +- 1.5 ms| 85.2 ms +- 0.3 ms | 99.8% l-d-r | x | x | 202.6 ms +- 4.4 ms | 208.0 ms +- 4.0 ms | 102.7% status --copies: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 1.941 s +- 0.014 s| 1.930 s +- 0.009 s | 99.4% m-u | | x | 1.924 s +- 0.007 s| 1.950 s +- 0.010 s | 101.4% m-u | x | | 1.959 s +- 0.085 s| 1.926 s +- 0.009 s | 98.3% m-u | x | x | 96.2 ms +- 1.0 ms| 96.4 ms +- 0.7 ms | 100.2% l-d-r | | | 604.4 ms +- 10.6 ms | 602.6 ms +- 7.1 ms | 99.7% l-d-r | | x | 605.7 ms +- 4.1 ms | 607.4 ms +- 6.1 ms | 100.3% l-d-r | x | | 182.4 ms +- 1.2 ms | 183.4 ms +- 1.2 ms | 100.5% l-d-r | x | x | 150.8 ms +- 2.0 ms | 150.6 ms +- 1.0 ms | 99.9% update $rev^; ~/src/hg/hg{hg}/hg update $rev: repo | N | T | before (mean +- stdev) | after (mean +- stdev) | % of before --+---+---++---+ m-u | | | 3.185 s +- 0.027 s| 3.181 s +- 0.017 s | 99.9% m-u | | x | 3.028 s +- 0.021 s| 2.954 s +- 0.010 s | 97.6% m-u | x | | 3.168 s +- 0.010 s| 3.175 s +- 0.023 s | 100.2% m-u | x | x | 317.5 ms +- 3.5 ms | 313.2 ms +- 2.9 ms | 98.6% l-d-r | | | 456.2 ms +- 10.6 ms | 454.4 ms +- 5.8 ms | 99.6% l-d-r | | x | 9.236 s +- 0.063 s| 757.9 ms +- 9.2 ms | 8.2% <-- l-d-r | x | | 257.6 ms +- 2.3 ms | 261.2 ms +- 1.7 ms | 101.4% l-d-r | x |
D4874: treemanifests: store whether a lazydirs entry needs copied after materializing
spectral created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Due to the way that things like manifestlog.get caches its values, without making a copy (if necessary) after calling readsubtree(), we might end up adjusting the state of the same object on different contexts, breaking things like dirty state tracking (and probably other things). REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4874 AFFECTED FILES mercurial/manifest.py CHANGE DETAILS diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -701,15 +701,22 @@ return self._dir + path def _loadalllazy(self): -for k, (path, node, readsubtree) in self._lazydirs.iteritems(): -self._dirs[k] = readsubtree(path, node) +selfdirs = self._dirs +for d, (path, node, readsubtree, docopy) in self._lazydirs.iteritems(): +if docopy: +selfdirs[d] = readsubtree(path, node).copy() +else: +selfdirs[d] = readsubtree(path, node) self._lazydirs = {} def _loadlazy(self, d): v = self._lazydirs.get(d) if v: -path, node, readsubtree = v -self._dirs[d] = readsubtree(path, node) +path, node, readsubtree, docopy = v +if docopy: +self._dirs[d] = readsubtree(path, node).copy() +else: +self._dirs[d] = readsubtree(path, node) del self._lazydirs[d] def _loadchildrensetlazy(self, visit): @@ -1170,7 +1177,9 @@ for f, n, fl in _parse(text): if fl == 't': f = f + '/' -selflazy[f] = (subpath(f), n, readsubtree) +# False below means "doesn't need to be copied" and can use the +# cached value from readsubtree directly. +selflazy[f] = (subpath(f), n, readsubtree, False) elif '/' in f: # This is a flat manifest, so use __setitem__ and setflag rather # than assigning directly to _files and _flags, so we can @@ -1197,8 +1206,7 @@ """ self._load() flags = self.flags -lazydirs = [(d[:-1], node, 't') for -d, (path, node, readsubtree) in self._lazydirs.iteritems()] +lazydirs = [(d[:-1], v[1], 't') for d, v in self._lazydirs.iteritems()] dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs] files = [(f, self._files[f], flags(f)) for f in self._files] return _text(sorted(dirs + files + lazydirs)) To: spectral, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4873: treemanifests: extract _loaddifflazy from _diff, use in _filesnotin
spectral 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/D4873 AFFECTED FILES mercurial/manifest.py CHANGE DETAILS diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -724,6 +724,28 @@ loadlazy(k + '/') return visit +def _loaddifflazy(self, t1, t2): +"""load items in t1 and t2 if they're needed for diffing. + +The criteria currently is: +- if it's not present in _lazydirs in either t1 or t2, load it in the + other (it may already be loaded or it may not exist, doesn't matter) +- if it's present in _lazydirs in both, compare the nodeid; if it + differs, load it in both +""" +toloadlazy = [] +for d, v1 in t1._lazydirs.iteritems(): +v2 = t2._lazydirs.get(d) +if not v2 or v2[1] != v1[1]: +toloadlazy.append(d) +for d, v1 in t2._lazydirs.iteritems(): +if d not in t1._lazydirs: +toloadlazy.append(d) + +for d in toloadlazy: +t1._loadlazy(d) +t2._loadlazy(d) + def __len__(self): self._load() size = len(self._files) @@ -957,8 +979,7 @@ return t1._load() t2._load() -t1._loadalllazy() -t2._loadalllazy() +self._loaddifflazy(t1, t2) for d, m1 in t1._dirs.iteritems(): if d in t2._dirs: m2 = t2._dirs[d] @@ -1113,18 +1134,7 @@ return t1._load() t2._load() -toloadlazy = [] -for d, v1 in t1._lazydirs.iteritems(): -v2 = t2._lazydirs.get(d) -if not v2 or v2[1] != v1[1]: -toloadlazy.append(d) -for d, v1 in t2._lazydirs.iteritems(): -if d not in t1._lazydirs: -toloadlazy.append(d) - -for d in toloadlazy: -t1._loadlazy(d) -t2._loadlazy(d) +self._loaddifflazy(t1, t2) for d, m1 in t1._dirs.iteritems(): m2 = t2._dirs.get(d, emptytree) To: spectral, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4872: identify: show remote bookmarks in `hg id url -Tjson -B`
valentin.gatienbaron created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I didn't display bookmarks when `default and not ui.quiet`: it seems strange for templates to depend on --id or -q, and it would take more code for `hg id url -T {node}` to not request remote bookmarks. An alternative I thought of was providing lazy data to the formatter, `fm.data(bookmarks=lambda: fm.formatlist(getbms(), name='bookmark'))`. The plainformatter would naturally not compute it, the templateformatter would compute only what it needs, and the other ones would compute everything, but that's not supported (or I don't see how), so I abandoned this idea. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4872 AFFECTED FILES mercurial/commands.py CHANGE DETAILS diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -3044,7 +3044,7 @@ output.append(bm) else: fm.data(node=hex(remoterev)) -if 'bookmarks' in fm.datahint(): +if bookmarks or 'bookmarks' in fm.datahint(): fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark')) else: if rev: To: valentin.gatienbaron, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4859: exchangev2: add progress bar around manifest scanning
indygreg updated this revision to Diff 11655. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4859?vs=11651=11655 REVISION DETAIL https://phab.mercurial-scm.org/D4859 AFFECTED FILES mercurial/exchangev2.py CHANGE DETAILS diff --git a/mercurial/exchangev2.py b/mercurial/exchangev2.py --- a/mercurial/exchangev2.py +++ b/mercurial/exchangev2.py @@ -320,19 +320,25 @@ ml = repo.manifestlog fnodes = collections.defaultdict(dict) -for manifestnode in manifestnodes: -m = ml.get(b'', manifestnode) +progress = repo.ui.makeprogress( +_('scanning manifests'), total=len(manifestnodes)) + +with progress: +for manifestnode in manifestnodes: +m = ml.get(b'', manifestnode) -# TODO this will pull in unwanted nodes because it takes the storage -# delta into consideration. What we really want is something that takes -# the delta between the manifest's parents. And ideally we would -# ignore file nodes that are known locally. For now, ignore both -# these limitations. This will result in incremental fetches requesting -# data we already have. So this is far from ideal. -md = m.readfast() +# TODO this will pull in unwanted nodes because it takes the storage +# delta into consideration. What we really want is something that +# takes the delta between the manifest's parents. And ideally we +# would ignore file nodes that are known locally. For now, ignore +# both these limitations. This will result in incremental fetches +# requesting data we already have. So this is far from ideal. +md = m.readfast() -for path, fnode in md.items(): -fnodes[path].setdefault(fnode, manifestnode) +for path, fnode in md.items(): +fnodes[path].setdefault(fnode, manifestnode) + +progress.increment() return fnodes To: 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
D4870: showstack: also handle SIGALRM
durin42 created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This is looking *very* handy when debugging mysterious hangs in a test: you can wrap a hanging invocation in `perl -e 'alarm shift @ARGV; exec @ARGV' 1` for example, a hanging `hg pull` becomes `perl -e 'alarm shift @ARGV; exec @ARGV' 1 hg pull` where the `1` is the timeout in seconds before the process will be hit with SIGALRM. After making that edit to the test file, you can then use --extra-config-opt on run-tests.py to globaly enable showstack during the test run, so you'll get full stack traces as you force your hg to exit. I wonder (but only a little, not enough to take action just yet) if we should wire up some scaffolding in run-tests itself to automatically wrap all commands in alarm(3) somehow to avoid hangs in the future? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4870 AFFECTED FILES contrib/showstack.py CHANGE DETAILS diff --git a/contrib/showstack.py b/contrib/showstack.py --- a/contrib/showstack.py +++ b/contrib/showstack.py @@ -4,7 +4,7 @@ """dump stack trace when receiving SIGQUIT (Ctrl-\) and SIGINFO (Ctrl-T on BSDs) """ -from __future__ import absolute_import +from __future__ import absolute_import, print_function import signal import sys import traceback @@ -14,8 +14,14 @@ traceback.print_stack(args[1], limit=10, file=sys.stderr) sys.stderr.write("\n") +def sigexit(*args): +sigshow(*args) +print('alarm!') +sys.exit(1) + def extsetup(ui): signal.signal(signal.SIGQUIT, sigshow) +signal.signal(signal.SIGALRM, sigexit) try: signal.signal(signal.SIGINFO, sigshow) except AttributeError: To: durin42, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4860: repository: define and use revision flag constants
indygreg updated this revision to Diff 11652. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4860?vs=11638=11652 REVISION DETAIL https://phab.mercurial-scm.org/D4860 AFFECTED FILES mercurial/changegroup.py mercurial/help/internals/changegroups.txt mercurial/repository.py mercurial/revlogutils/constants.py mercurial/testing/storage.py tests/simplestorerepo.py tests/test-help.t CHANGE DETAILS diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -1038,8 +1038,8 @@ There are 3 versions of changegroups: "1", "2", and "3". From a high- level, versions "1" and "2" are almost exactly the same, with the only - difference being an additional item in the *delta header*. Version "3" - adds support for revlog flags in the *delta header* and optionally + difference being an additional item in the *delta header*. Version "3" + adds support for storage flags in the *delta header* and optionally exchanging treemanifests (enabled by setting an option on the "changegroup" part in the bundle2). @@ -1162,6 +1162,27 @@ changegroup. This allows the delta to be expressed against any parent, which can result in smaller deltas and more efficient encoding of data. + The *flags* field holds bitwise flags affecting the processing of revision + data. The following flags are defined: + + 32768 + Censored revision. The revision's fulltext has been replaced by censor + metadata. May only occur on file revisions. + + 16384 + Ellipsis revision. Revision hash does not match data (likely due to + rewritten parents). + + 8192 + Externally stored. The revision fulltext contains "key:value" "\n" + delimited metadata defining an object stored elsewhere. Used by the LFS + extension. + + For historical reasons, the integer values are identical to revlog version + 1 per-revision storage flags and correspond to bits being set in this + 2-byte field. Bits were allocated starting from the most-significant bit, + hence the reverse ordering and allocation of these flags. + Changeset Segment = @@ -3435,8 +3456,8 @@ There are 3 versions of changegroups: 1, 2, and 3. From a high-level, versions 1 and 2 are almost exactly the same, with the - only difference being an additional item in the *delta header*. Version - 3 adds support for revlog flags in the *delta header* and optionally + only difference being an additional item in the *delta header*. Version + 3 adds support for storage flags in the *delta header* and optionally exchanging treemanifests (enabled by setting an option on the changegroup part in the bundle2). @@ -3582,6 +3603,24 @@ changegroup. This allows the delta to be expressed against any parent, which can result in smaller deltas and more efficient encoding of data. + + The *flags* field holds bitwise flags affecting the processing of revision + data. The following flags are defined: + + + 32768 + Censored revision. The revision's fulltext has been replaced by censor metadata. May only occur on file revisions. + 16384 + Ellipsis revision. Revision hash does not match data (likely due to rewritten parents). + 8192 + Externally stored. The revision fulltext contains key:value \n delimited metadata defining an object stored elsewhere. Used by the LFS extension. + + + For historical reasons, the integer values are identical to revlog version 1 + per-revision storage flags and correspond to bits being set in this 2-byte + field. Bits were allocated starting from the most-significant bit, hence the + reverse ordering and allocation of these flags. + Changeset Segment The *changeset segment* consists of a single *delta group* holding diff --git a/tests/simplestorerepo.py b/tests/simplestorerepo.py --- a/tests/simplestorerepo.py +++ b/tests/simplestorerepo.py @@ -371,7 +371,7 @@ def iscensored(self, rev): validaterev(rev) -return self._flags(rev) & revlog.REVIDX_ISCENSORED +return self._flags(rev) & repository.REVISION_FLAG_CENSORED def commonancestorsheads(self, a, b): validatenode(a) diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -17,7 +17,7 @@ from .. import ( error, mdiff, -revlog, +repository, ) from ..utils import ( storageutil, @@ -874,7 +874,7 @@ with self._maketransactionfn() as tr: node0 = f.add(b'foo', None, tr, 0, nullid, nullid) f.addrevision(stored1, tr, 1, node0, nullid, - flags=revlog.REVIDX_ISCENSORED) + flags=repository.REVISION_FLAG_CENSORED)
Re: [PATCH 1 of 8 V3] context: refactor introrev to make the next patch easier to read
Oh, and thanks for splitting this patch up! I find it much easier to see what's going on now. On Wed, Oct 3, 2018 at 12:23 PM Martin von Zweigbergk wrote: > > > On Wed, Oct 3, 2018 at 12:15 PM Boris Feld wrote: > >> # HG changeset patch >> # User Boris Feld >> # Date 1538554251 -7200 >> # Wed Oct 03 10:10:51 2018 +0200 >> # Node ID 68ec0cf339c7e65ee4349f543e3024068fbbe591 >> # Parent 1a4c1a3cc3f5b54de7f56753c0ea8b02b4443958 >> # EXP-Topic copy-perf >> # Available At https://bitbucket.org/octobus/mercurial-devel/ >> # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r >> 68ec0cf339c7 >> context: refactor introrev to make the next patch easier to read >> >> We are about to update the logic for checking if the rev is available in >> an >> efficient manner. Refactoring introrev will make the next patch easier to >> read. >> >> diff --git a/mercurial/context.py b/mercurial/context.py >> --- a/mercurial/context.py >> +++ b/mercurial/context.py >> @@ -776,10 +776,15 @@ class basefilectx(object): >> """ >> lkr = self.linkrev() >> attrs = vars(self) >> -noctx = not (r'_changeid' in attrs or r'_changectx' in attrs) >> -if noctx or self.rev() == lkr: >> -return self.linkrev() >> -return self._adjustlinkrev(self.rev(), inclusive=True) >> +lazyavailable = r'_changeid' in attrs or r'_changectx' in attrs >> +if lazyavailable: >> +rev = self.rev() >> +if rev == lkr: >> +return rev >> +else: >> +return self._adjustlinkrev(rev, inclusive=True) >> +else: >> +return self.rev() >> > > For this to be a pure refactoring, shouldn't this be "return > self.linkrev()"? That's what we used to return when noctx was true. > > >> def introfilectx(self): >> """Return filectx having identical contents, but pointing to the >> ___ >> Mercurial-devel mailing list >> Mercurial-devel@mercurial-scm.org >> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >> > ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 1 of 8 V3] context: refactor introrev to make the next patch easier to read
On Wed, Oct 3, 2018 at 12:15 PM Boris Feld wrote: > # HG changeset patch > # User Boris Feld > # Date 1538554251 -7200 > # Wed Oct 03 10:10:51 2018 +0200 > # Node ID 68ec0cf339c7e65ee4349f543e3024068fbbe591 > # Parent 1a4c1a3cc3f5b54de7f56753c0ea8b02b4443958 > # EXP-Topic copy-perf > # Available At https://bitbucket.org/octobus/mercurial-devel/ > # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r > 68ec0cf339c7 > context: refactor introrev to make the next patch easier to read > > We are about to update the logic for checking if the rev is available in an > efficient manner. Refactoring introrev will make the next patch easier to > read. > > diff --git a/mercurial/context.py b/mercurial/context.py > --- a/mercurial/context.py > +++ b/mercurial/context.py > @@ -776,10 +776,15 @@ class basefilectx(object): > """ > lkr = self.linkrev() > attrs = vars(self) > -noctx = not (r'_changeid' in attrs or r'_changectx' in attrs) > -if noctx or self.rev() == lkr: > -return self.linkrev() > -return self._adjustlinkrev(self.rev(), inclusive=True) > +lazyavailable = r'_changeid' in attrs or r'_changectx' in attrs > +if lazyavailable: > +rev = self.rev() > +if rev == lkr: > +return rev > +else: > +return self._adjustlinkrev(rev, inclusive=True) > +else: > +return self.rev() > For this to be a pure refactoring, shouldn't this be "return self.linkrev()"? That's what we used to return when noctx was true. > def introfilectx(self): > """Return filectx having identical contents, but pointing to the > ___ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel > ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 8 of 8 V3] copies: add time information to the debug information
# HG changeset patch # User Boris Feld # Date 1536335028 14400 # Fri Sep 07 11:43:48 2018 -0400 # Node ID 5780db5f3a36b6450899b21c8fd033a348eee7db # Parent 2b5e633c984e0341b2606e66cfafc92f27c10876 # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 5780db5f3a36 copies: add time information to the debug information diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -204,11 +204,16 @@ def _committedforwardcopies(a, b, match) fctx = b[f] fctx._ancestrycontext = ancestrycontext +if debug: +start = util.timer() ofctx = _tracefile(fctx, am, limit) if ofctx: if debug: dbg('debug.copies: rename of: %s\n' % ofctx._path) cm[f] = ofctx.path() +if debug: +dbg('debug.copies: time: %s seconds\n' +% (util.timer() - start)) return cm def _forwardcopies(a, b, match=None): diff --git a/tests/test-mv-cp-st-diff.t b/tests/test-mv-cp-st-diff.t --- a/tests/test-mv-cp-st-diff.t +++ b/tests/test-mv-cp-st-diff.t @@ -1676,6 +1676,7 @@ Check debug output for copy tracing debug.copies: missing file to search: 1 debug.copies:tracing file: renamed debug.copies: rename of: f + debug.copies: time: * seconds (glob) A renamed f R f ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 7 of 8 V3] copies: add a devel debug mode to trace what copy tracing does
# HG changeset patch # User Boris Feld # Date 153666 14400 # Fri Sep 07 11:16:06 2018 -0400 # Node ID 2b5e633c984e0341b2606e66cfafc92f27c10876 # Parent fd0da35824d09d0a0fa66a23627dd24209c6c3b1 # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 2b5e633c984e copies: add a devel debug mode to trace what copy tracing does Mercurial can spend a lot of time finding renames between two commits. Having more information about that process help (eg: many files vs 1 file, etc...) diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -377,6 +377,9 @@ coreconfigitem('devel', 'user.obsmarker' coreconfigitem('devel', 'warn-config-unknown', default=None, ) +coreconfigitem('devel', 'debug.copies', +default=False, +) coreconfigitem('devel', 'debug.extensions', default=False, ) diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -163,9 +163,17 @@ def _committedforwardcopies(a, b, match) """Like _forwardcopies(), but b.rev() cannot be None (working copy)""" # files might have to be traced back to the fctx parent of the last # one-side-only changeset, but not further back than that -limit = _findlimit(a._repo, a.rev(), b.rev()) +repo = a._repo +debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies') +dbg = repo.ui.debug +if debug: +dbg('debug.copies:looking into rename from %s to %s\n' +% (a, b)) +limit = _findlimit(repo, a.rev(), b.rev()) if limit is None: limit = -1 +if debug: +dbg('debug.copies: search limit: %d\n' % limit) am = a.manifest() # find where new files came from @@ -186,11 +194,20 @@ def _committedforwardcopies(a, b, match) missing = _computeforwardmissing(a, b, match=forwardmissingmatch) ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True) + +if debug: +dbg('debug.copies: missing file to search: %d\n' % len(missing)) + for f in missing: +if debug: +dbg('debug.copies:tracing file: %s\n' % f) fctx = b[f] fctx._ancestrycontext = ancestrycontext + ofctx = _tracefile(fctx, am, limit) if ofctx: +if debug: +dbg('debug.copies: rename of: %s\n' % ofctx._path) cm[f] = ofctx.path() return cm @@ -226,13 +243,24 @@ def _backwardrenames(a, b): def pathcopies(x, y, match=None): """find {dst@y: src@x} copy mapping for directed compare""" +repo = x._repo +debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies') +if debug: +repo.ui.debug('debug.copies: searching copies from %s to %s\n' + % (x, y)) if x == y or not x or not y: return {} a = y.ancestor(x) if a == x: +if debug: +repo.ui.debug('debug.copies: search mode: forward\n') return _forwardcopies(x, y, match=match) if a == y: +if debug: +repo.ui.debug('debug.copies: search mode: backward\n') return _backwardrenames(x, y) +if debug: +repo.ui.debug('debug.copies: search mode: combined\n') return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y, match=match)) diff --git a/tests/test-mv-cp-st-diff.t b/tests/test-mv-cp-st-diff.t --- a/tests/test-mv-cp-st-diff.t +++ b/tests/test-mv-cp-st-diff.t @@ -1666,4 +1666,18 @@ accessing the parent of 4 (renamed) shou @@ -0,0 +1,1 @@ +change +Check debug output for copy tracing + + $ hg status --copies --rev 'desc(dev)' --rev . --config devel.debug.copies=yes --debug + debug.copies: searching copies from a51f36ab1704 to 7935fd48a8f9 + debug.copies: search mode: forward + debug.copies:looking into rename from a51f36ab1704 to 7935fd48a8f9 + debug.copies: search limit: 2 + debug.copies: missing file to search: 1 + debug.copies:tracing file: renamed + debug.copies: rename of: f + A renamed +f + R f + $ cd .. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 6 of 8 V3] context: floor adjustlinkrev graph walk during copy tracing
# HG changeset patch # User Boris Feld # Date 1536255188 14400 # Thu Sep 06 13:33:08 2018 -0400 # Node ID fd0da35824d09d0a0fa66a23627dd24209c6c3b1 # Parent 964fbe39ab182eb0d65830dc87ef39d0382a41fa # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r fd0da35824d0 context: floor adjustlinkrev graph walk during copy tracing The `_adjustlinkrev` method gains an optional "stoprev" argument. The linkrev adjustment will give up once this floor is reached. The relevant functions using `_adjustlinkrev` are updated to pass an appropriate value in the copy tracing code. In some private repository, about 10% of the status call triggered pathological case addressed by this change. The speedup varies from one call to another, the best-observed win is moving from 170s to 11s. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -569,7 +569,7 @@ class basefilectx(object): def _changeid(self): return self._findchangerev() -def _findchangerev(self): +def _findchangerev(self, stoprev=None): if r'_changeid' in self.__dict__: changeid = self._changeid elif r'_changectx' in self.__dict__: @@ -577,10 +577,11 @@ class basefilectx(object): elif r'_descendantrev' in self.__dict__: # this file context was created from a revision with a known # descendant, we can (lazily) correct for linkrev aliases -changeid = self._adjustlinkrev(self._descendantrev) +changeid = self._adjustlinkrev(self._descendantrev, stoprev=stoprev) else: changeid = self._filelog.linkrev(self._filerev) -self._changeid = changeid +if changeid is not None: +self._changeid = changeid return changeid @propertycache @@ -724,7 +725,7 @@ class basefilectx(object): return True -def _adjustlinkrev(self, srcrev, inclusive=False): +def _adjustlinkrev(self, srcrev, inclusive=False, stoprev=None): """return the first ancestor of introducing If the linkrev of the file revision does not point to an ancestor of @@ -733,6 +734,10 @@ class basefilectx(object): :srcrev: the changeset revision we search ancestors from :inclusive: if true, the src revision will also be checked +:stoprev: an optional revision to stop the walk at. If no introduction + of this file content could be found before this floor + revision, the function will returns "None" and stops its + iteration. """ repo = self._repo cl = repo.unfiltered().changelog @@ -758,6 +763,8 @@ class basefilectx(object): fnode = self._filenode path = self._path for a in iteranc: +if stoprev is not None and a < stoprev: +return None ac = cl.read(a) # get changeset data (we avoid object creation) if path in ac[3]: # checking the 'files' field. # The file has been touched, check if the content is @@ -773,8 +780,12 @@ class basefilectx(object): def isintroducedafter(self, changelogrev): """True if a filectx have been introduced after a given floor revision """ -return (changelogrev <= self.linkrev() -or changelogrev <= self._introrev()) +if changelogrev <= self.linkrev(): +return True +introrev = self._introrev(stoprev=changelogrev) +if introrev is None: +return False +return changelogrev <= introrev def _lazyrevavailable(self): """return True if self.rev() is available without computation, @@ -804,7 +815,15 @@ class basefilectx(object): """ return self._introrev() -def _introrev(self): +def _introrev(self, stoprev=None): +""" +Same as `introrev` but, with an extra argument to limit changelog +iteration range in some internal usecase. + +If `stoprev` is set, the `introrev` will not be searched past that +`stoprev` revision and "None" might be returned. This is useful to +limit iteration range. +""" lkr = self.linkrev() attrs = vars(self) lazyavailable = self._lazyrevavailable() @@ -813,9 +832,10 @@ class basefilectx(object): if rev == lkr: return rev else: -return self._adjustlinkrev(rev, inclusive=True) +return self._adjustlinkrev(rev, inclusive=True, + stoprev=stoprev) else: -return self._findchangerev() +return self._findchangerev(stoprev=stoprev) def introfilectx(self): """Return filectx having identical contents, but
[PATCH 4 of 8 V3] context: split `introrev` logic in a sub function
# HG changeset patch # User Boris Feld # Date 1536255508 14400 # Thu Sep 06 13:38:28 2018 -0400 # Node ID 759af590645e2f0cc6fbed2b7b97ed718e277dc5 # Parent eb46ac11a9a81400e96ab3db00a38944a864100f # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 759af590645e context: split `introrev` logic in a sub function We want to add a mechanism to stop iteration early associated to intro rev early in some case. However, it does not make sense to expose it in the public `filectx` API. So we split the code into an internal method instead. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -769,7 +769,7 @@ class basefilectx(object): """True if a filectx have been introduced after a given floor revision """ return (changelogrev <= self.linkrev() -or changelogrev <= self.introrev()) +or changelogrev <= self._introrev()) def _lazyrevavailable(self): """return True if self.rev() is available without computation, @@ -797,6 +797,9 @@ class basefilectx(object): 'linkrev-shadowing' when a file revision is used by multiple changesets. """ +return self._introrev() + +def _introrev(self): lkr = self.linkrev() attrs = vars(self) lazyavailable = self._lazyrevavailable() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 8 V3] context: introduce an `isintroducedafter` method and use it in copies
# HG changeset patch # User Boris Feld # Date 1536252767 14400 # Thu Sep 06 12:52:47 2018 -0400 # Node ID eb46ac11a9a81400e96ab3db00a38944a864100f # Parent 355a69e274a2ec851c6d13cfbbac45f5e197294c # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r eb46ac11a9a8 context: introduce an `isintroducedafter` method and use it in copies Right now, copy tracing make effort to not traverse the graph too much to save performance. It uses a "limit" acting as a floor revision past which data are no longer relevant to the current copy tracing. However, to enforce this limit, it uses a call to `filectx.rev()`, that call can trigger a graph traversal on its own. That extra graph traversal is oblivious of the current limit and can become very expensive. That cost is increased by the nature of work done in adjust link rev, we are not only walking down the graph, we are also checking the affected file for each revision we walk through. Something significantly more expensive than the walk itself. To work around this we need to make the `filectx` operation aware of the current limit. The first step is to introduce a dedicated method: `isintroducedafter`. We'll then rework that method logic to stop traversal as soon as possible. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -765,6 +765,12 @@ class basefilectx(object): # result is crash somewhere else at to some point. return lkr +def isintroducedafter(self, changelogrev): +"""True if a filectx have been introduced after a given floor revision +""" +return (changelogrev <= self.linkrev() +or changelogrev <= self.introrev()) + def _lazyrevavailable(self): """return True if self.rev() is available without computation, diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -139,7 +139,7 @@ def _tracefile(fctx, am, limit=-1): for f in fctx.ancestors(): if am.get(f.path(), None) == f.filenode(): return f -if limit >= 0 and f.linkrev() < limit and f.rev() < limit: +if limit >= 0 and not f.isintroducedafter(limit): return None def _dirstatecopies(d, match=None): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 8 V3] context: refactor introrev to make the next patch easier to read
# HG changeset patch # User Boris Feld # Date 1538554251 -7200 # Wed Oct 03 10:10:51 2018 +0200 # Node ID 68ec0cf339c7e65ee4349f543e3024068fbbe591 # Parent 1a4c1a3cc3f5b54de7f56753c0ea8b02b4443958 # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 68ec0cf339c7 context: refactor introrev to make the next patch easier to read We are about to update the logic for checking if the rev is available in an efficient manner. Refactoring introrev will make the next patch easier to read. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -776,10 +776,15 @@ class basefilectx(object): """ lkr = self.linkrev() attrs = vars(self) -noctx = not (r'_changeid' in attrs or r'_changectx' in attrs) -if noctx or self.rev() == lkr: -return self.linkrev() -return self._adjustlinkrev(self.rev(), inclusive=True) +lazyavailable = r'_changeid' in attrs or r'_changectx' in attrs +if lazyavailable: +rev = self.rev() +if rev == lkr: +return rev +else: +return self._adjustlinkrev(rev, inclusive=True) +else: +return self.rev() def introfilectx(self): """Return filectx having identical contents, but pointing to the ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 8 V3] context: fix introrev to avoid computation as initially intended
# HG changeset patch # User Boris Feld # Date 1536254177 14400 # Thu Sep 06 13:16:17 2018 -0400 # Node ID 355a69e274a2ec851c6d13cfbbac45f5e197294c # Parent 68ec0cf339c7e65ee4349f543e3024068fbbe591 # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 355a69e274a2 context: fix introrev to avoid computation as initially intended The `filerev.introrev()` method has various logic be as efficient as possible. In particular, it tries to restrict the range covered by the `ctx._adjustlinkrev(...)` call. However, it does so using the value returned by `ctx.rev()`. In some case (eg: copy tracing), that `ctx.rev()` call would do an `_adjustlinkrev(...)` call on its own, defeating the optimization purpose and doing the computation twice. We are about to improve graph traversal associated with copy tracing using code based on `ctx.introrev()`, so we need this fixed before proceeding further. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -765,6 +765,23 @@ class basefilectx(object): # result is crash somewhere else at to some point. return lkr +def _lazyrevavailable(self): +"""return True if self.rev() is available without computation, + +If finding the rev would trigger a possibly expensive computation, we +return False.""" +attrs = vars(self) +if r'_changeid' in attrs: +# We have a cached value already +return True +elif r'_changectx' in attrs: +# We know which changelog entry we are coming from +return True +elif r'_descendantrev' not in attrs: +# we have no context, so linkrev will be used +return True +return False + def introrev(self): """return the rev of the changeset which introduced this file revision @@ -776,7 +793,7 @@ class basefilectx(object): """ lkr = self.linkrev() attrs = vars(self) -lazyavailable = r'_changeid' in attrs or r'_changectx' in attrs +lazyavailable = self._lazyrevavailable() if lazyavailable: rev = self.rev() if rev == lkr: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4859: exchangev2: add progress bar around manifest scanning
indygreg updated this revision to Diff 11651. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4859?vs=11637=11651 REVISION DETAIL https://phab.mercurial-scm.org/D4859 AFFECTED FILES mercurial/exchangev2.py CHANGE DETAILS diff --git a/mercurial/exchangev2.py b/mercurial/exchangev2.py --- a/mercurial/exchangev2.py +++ b/mercurial/exchangev2.py @@ -320,19 +320,25 @@ ml = repo.manifestlog fnodes = collections.defaultdict(dict) -for manifestnode in manifestnodes: -m = ml.get(b'', manifestnode) +progress = repo.ui.makeprogress( +_('scanning manifests'), total=len(manifestnodes)) + +with progress: +for manifestnode in manifestnodes: +m = ml.get(b'', manifestnode) -# TODO this will pull in unwanted nodes because it takes the storage -# delta into consideration. What we really want is something that takes -# the delta between the manifest's parents. And ideally we would -# ignore file nodes that are known locally. For now, ignore both -# these limitations. This will result in incremental fetches requesting -# data we already have. So this is far from ideal. -md = m.readfast() +# TODO this will pull in unwanted nodes because it takes the storage +# delta into consideration. What we really want is something that takes +# the delta between the manifest's parents. And ideally we would +# ignore file nodes that are known locally. For now, ignore both +# these limitations. This will result in incremental fetches requesting +# data we already have. So this is far from ideal. +md = m.readfast() -for path, fnode in md.items(): -fnodes[path].setdefault(fnode, manifestnode) +for path, fnode in md.items(): +fnodes[path].setdefault(fnode, manifestnode) + +progress.increment() return fnodes To: 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
D4859: exchangev2: add progress bar around manifest scanning
indygreg added inline comments. INLINE COMMENTS > martinvonz wrote in exchangev2.py:341 > should call `progress.complete()` here to remove the progress bar (or use it > as a context manager) Yes we should. I'll send an updated patch in a few minutes. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4859 To: 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
D4813: narrow: the first version of narrow_widen wireprotocol command
indygreg added inline comments. INLINE COMMENTS > narrowbundle2.py:329 > getbundleargs['narrow'] = 'boolean' > -getbundleargs['widen'] = 'boolean' > getbundleargs['depth'] = 'plain' Because of how `wireprotov1server.getbundle()` handles arguments, removing this argument from the definition of arguments to the `getbundle` wire protocol command means that existing clients attempting to send the argument to the `getbundle` wire protocol command will cause a server-side exception. We need to keep this key around for BC unless we're fine breaking old clients or unless there is something that changes server capabilities in a way that prevents old clients from calling `getbundle` with this argument. We could ignore the argument or nerf the server to error if it is received. > martinvonz wrote in narrowwirepeer.py:79-82 > That code was added in > https://www.mercurial-scm.org/repo/hg/rev/3e7f675628ad. Maybe @indygreg can > tell us if he thinks we should support the same values for a new wireprotocol > command. We probably want to extract the argument "parsing" from `wireprotov1server.getbundle()` into a standalone function so we can use it for this command. Arguments in wire protocol version 1 are wonky :/ > narrowwirepeer.py:64 > +""" > + > +oldincludes = wireprototypes.decodelist(args.get('oldincludes')) I'm pretty sure we'll want a try..except around this entire command so that we'll send a bundle2 error payload on failure. See `wireprotov1server.getbundle()` for inspiration. Search for use of the `error:abort` bundle2 part. We'll want to do something like that in the except block. Keep in mind exceptions can occur mid stream. In that case, proper error handling is a bit harder. But a good start is to put a `try..except` around all the code before we `return` the output. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4813 To: pulkit, durin42, #hg-reviewers, martinvonz Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4859: exchangev2: add progress bar around manifest scanning
martinvonz added inline comments. INLINE COMMENTS > exchangev2.py:341 > +progress.increment() > + > return fnodes should call `progress.complete()` here to remove the progress bar (or use it as a context manager) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4859 To: 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
Re: [PATCH 3 of 7] context: fix introrev to avoid computation as initially intended
Btw, just because I found this patch hard to follow doesn't necessarily mean that others do. I won't mind if someone else understands what it does and queues it (and a third person adds a second accept stamp). On Tue, Oct 2, 2018 at 5:46 AM Martin von Zweigbergk wrote: > > > On Tue, Oct 2, 2018, 01:58 Boris FELD wrote: > >> >> On 01/10/2018 18:43, Martin von Zweigbergk via Mercurial-devel wrote: >> >> >> >> On Mon, Oct 1, 2018 at 9:11 AM Boris FELD wrote: >> >>> On 10/09/2018 18:21, Martin von Zweigbergk via Mercurial-devel wrote: >>> >>> >>> >>> On Fri, Sep 7, 2018 at 8:09 AM Boris Feld >>> wrote: >>> # HG changeset patch # User Boris Feld # Date 1536254177 14400 # Thu Sep 06 13:16:17 2018 -0400 # Node ID a4c3eb6c1a36cbbf64fa8930b173154b2e77ef2b # Parent 9a18509c522deeb62a7b244dcf4c7b79a8dc1132 # EXP-Topic copy-perf # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r a4c3eb6c1a36 context: fix introrev to avoid computation as initially intended diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -829,6 +829,23 @@ class basefilectx(object): # result is crash somewhere else at to some point. return lkr +def _lazyrev(self): >>> >>> We usually try to separate refactoring (like extracting a method) from >>> functional (or performance-related) changes. Could you do that with this >>> patch or does it not make sense for some reason? >>> >>> In this case, the two changes are a bit too interleaved to be easily >>> split in two. We can't do the special casing until we have the new method >>> and the new method can't have any caller without changing the conditionals >>> to include the special casing. >>> >> >> Maybe I missed something, but it looks like _lazyrev() returns either >> "None" or "self.rev()", even at the end of the series. Did I get that >> right? Will that change later? If not, it seems like you could instead >> extract a method that calculates what's currently called "noctx". Even if >> that's going to change, it might make it easier to understand this patch if >> you split out a patch that made the structure here more similar to your >> goal, something like: >> >> >> @@ -837,9 +837,13 @@ class basefilectx(object): >> lkr = self.linkrev() >> attrs = vars(self) >> noctx = not (r'_changeid' in attrs or r'_changectx' in attrs) >> -if noctx or self.rev() == lkr: >> +if noctx: >> return self.linkrev() >> -return self._adjustlinkrev(self.rev(), inclusive=True) >> +else: >> +if self.rev() == lkr: >> +return self.linkrev() >> +else: >> +return self._adjustlinkrev(self.rev(), inclusive=True) >> >> Yes, you are right, we can split the changeset in two. >> >> I don't feel that it would help the readability of the series but I'm not >> the reviewer. Do you think it would help you review the patch? >> >> > Yes, I think it would. I did it myself in order to understand this patch. > I think it would also help to make that return just a boolean value, > assuming that will still work with later patches. Thanks. > > > >> >>> >>> ___ >>> Mercurial-devel mailing >>> listMercurial-devel@mercurial-scm.orghttps://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >>> >>> >> ___ >> Mercurial-devel mailing >> listMercurial-devel@mercurial-scm.orghttps://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >> >> ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4869: revlog: rewrite censoring logic
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I was able to corrupt a revlog relatively easily with the existing censoring code. The underlying problem is that the existing code doesn't fully take delta chains into account. When copying revisions that occur after the censored revision, the delta base can refer to a censored revision. Then at read time, things blow up due to the revision data not being a compressed delta. This commit rewrites the revlog censoring code to take a higher-level approach. We now create a new revlog instance pointing at temp files. We iterate through each revision in the source revlog and insert those revisions into the new revlog, replacing the censored revision's data along the way. The new implementation isn't as efficient as the old one. This is because it will fully engage delta computation on insertion. But I don't think it matters. The new implementation is a bit hacky because it attempts to reload the revlog instance with a new revlog index/data file. This is fragile. But this is needed because the index (which could be backed by C) would have a cached copy of the old, possibly changed data and that could lead to problems accessing index or revision data later. One benefit of the new approach is that we integrate with the transaction. The old revlog is backed up and if the transaction is rolled back, the original revlog is restored. As part of this, we had to teach the transaction about the store vfs. I'm not super keen about this. But this was the easiest way to hook things up to the transaction. We /could/ just ignore the transaction like we were doing before. But any file mutation should be governed by transaction semantics, including undo during rollback. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4869 AFFECTED FILES mercurial/filelog.py mercurial/localrepo.py mercurial/revlog.py mercurial/testing/storage.py tests/test-storage.py CHANGE DETAILS diff --git a/tests/test-storage.py b/tests/test-storage.py --- a/tests/test-storage.py +++ b/tests/test-storage.py @@ -30,7 +30,7 @@ return fl def maketransaction(self): -vfsmap = {'plain': STATE['vfs']} +vfsmap = {'plain': STATE['vfs'], 'store': STATE['vfs']} return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap, b'journal', b'undo') diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -1175,14 +1175,9 @@ self.assertEqual(list(f.revs()), [0, 1, 2]) self.assertEqual(f.read(node0), b'foo\n' * 30) +self.assertEqual(f.read(node2), b'foo\n' * 32) -# TODO revlog can't resolve revision after censor. Probably due to a -# cache on the revlog instance. -with self.assertRaises(error.StorageError): -self.assertEqual(f.read(node2), b'foo\n' * 32) - -# TODO should raise CensoredNodeError, but fallout from above prevents. -with self.assertRaises(error.StorageError): +with self.assertRaises(error.CensoredNodeError): f.read(node1) def testgetstrippointnoparents(self): diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2341,94 +2341,69 @@ destrevlog._lazydeltabase = oldlazydeltabase destrevlog._deltabothparents = oldamd -def censorrevision(self, node, tombstone=b''): +def censorrevision(self, tr, censornode, tombstone=b''): if (self.version & 0x) == REVLOGV0: raise error.RevlogError(_('cannot censor with version %d revlogs') % self.version) -rev = self.rev(node) +censorrev = self.rev(censornode) tombstone = storageutil.packmeta({b'censored': tombstone}, b'') -if len(tombstone) > self.rawsize(rev): +if len(tombstone) > self.rawsize(censorrev): raise error.Abort(_('censor tombstone must be no longer than ' 'censored data')) -# Using two files instead of one makes it easy to rewrite entry-by-entry -idxread = self.opener(self.indexfile, 'r') -idxwrite = self.opener(self.indexfile, 'wb', atomictemp=True) -if self.version & FLAG_INLINE_DATA: -dataread, datawrite = idxread, idxwrite -else: -dataread = self.opener(self.datafile, 'r') -datawrite = self.opener(self.datafile, 'wb', atomictemp=True) +# Rewriting the revlog in place is hard. Our strategy for censoring is +# to create a new revlog, copy all revisions to it, then replace the +# revlogs on transaction close. -# Copy all revlog data up to the entry to be
D4813: narrow: the first version of narrow_widen wireprotocol command
martinvonz added a comment. > I need to specify the arguments instead of just specifying "*". Don't forget this one. INLINE COMMENTS > pulkit wrote in narrowcommands.py:293-295 > We can have a 'exp-ellipses-2' which will tell whether the server supports > ellipses widening using narrow_widen() wireprotocol command or not. I think > that should help in the meantime. Also will a week, or 10-15 days be enough > for you? I think it will be better if can prevent releasing this > compatibility because exp-ellipses-1 was introduced in this cycle only. We don't really care whether a version is released or not. It would be nice to have a version of the client that would use the new wire protocol with ellipses if the server said it supported that but would otherwise fall back to the old getbundle-based call. > pulkit wrote in narrowwirepeer.py:49 > I implemented the peer initially in core only, but while implementing server > side, I realized it rely on logic in narrowbundle2.py which also needs to be > moved to core. Then I decided to implement it cleanly in the extension and > then move it to core. It seems to depend only on `widen_bundle`, which doesn't seem to depend on anything else, so it would probably be easy to move it to core, but I won't insist. > pulkit wrote in narrowwirepeer.py:79-82 > I am not sure, I just copied from getbundle() handling: > https://www.mercurial-scm.org/repo/hg/file/1a4c1a3cc3f5/mercurial/wireprotov1server.py#l404 That code was added in https://www.mercurial-scm.org/repo/hg/rev/3e7f675628ad. Maybe @indygreg can tell us if he thinks we should support the same values for a new wireprotocol command. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4813 To: pulkit, durin42, #hg-reviewers, martinvonz Cc: indygreg, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4867: revlog: clear revision cache on hash verification failure
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The revision cache is populated after raw revision fulltext is retrieved but before hash verification. If hash verification fails, the revision cache will be populated and subsequent operations to retrieve the invalid fulltext may return the cached fulltext instead of raising. This commit changes hash verification so it will invalidate the revision cache if the cached node fails hash verification. The side-effect is that subsequent operations to request the revision text - even the raw revision text - will always fail. The new behavior is consistent and is definitely less wrong. There is an open question of whether revision(raw=True) should validate hashes. But I'm going to punt on this problem. We can always change behavior later. And to be honest, I'm not sure we should expose raw=True on the storage interface at all. Another day... REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4867 AFFECTED FILES mercurial/revlog.py mercurial/testing/storage.py CHANGE DETAILS diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -881,13 +881,14 @@ with self.assertRaises(error.StorageError): f.revision(node1) -# revision(raw=True) still verifies hashes. -# TODO this is buggy because of cache interaction. -self.assertEqual(f.revision(node1, raw=True), fulltext1) +# raw=True still verifies because there are no special storage +# settings. +with self.assertRaises(error.StorageError): +f.revision(node1, raw=True) # read() behaves like revision(). -# TODO this is buggy because of cache interaction. -f.read(node1) +with self.assertRaises(error.StorageError): +f.read(node1) # We can't test renamed() here because some backends may not require # reading/validating the fulltext to return rename metadata. @@ -931,8 +932,8 @@ with self.assertRaises(error.StorageError): f.read(node1) -# TODO this should raise error.StorageError. -f.read(node1) +with self.assertRaises(error.StorageError): +f.read(node1) def testbadnodedelta(self): f = self._makefilefn() @@ -986,7 +987,8 @@ with self.assertRaises(error.CensoredNodeError): f.revision(1) -self.assertEqual(f.revision(1, raw=True), stored1) +with self.assertRaises(error.CensoredNodeError): +f.revision(1, raw=True) with self.assertRaises(error.CensoredNodeError): f.read(1) diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1659,6 +1659,15 @@ if p1 is None and p2 is None: p1, p2 = self.parents(node) if node != self.hash(text, p1, p2): +# Clear the revision cache on hash failure. The revision cache +# only stores the raw revision and clearing the cache does have +# the side-effect that we won't have a cache hit when the raw +# revision data is accessed. But this case should be rare and +# it is extra work to teach the cache about the hash +# verification state. +if self._revisioncache and self._revisioncache[0] == node: +self._revisioncache = None + revornode = rev if revornode is None: revornode = templatefilters.short(hex(node)) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4868: revlog: move loading of index data into own method
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This will allow us to "reload" a revlog instance from a rewritten index file, which will be used in a subsequent commit. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4868 AFFECTED FILES mercurial/revlog.py CHANGE DETAILS diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -415,12 +415,15 @@ raise error.RevlogError(_('revlog chunk cache size %r is not a ' 'power of 2') % self._chunkcachesize) +self._loadindex(v, mmapindexthreshold) + +def _loadindex(self, v, mmapindexthreshold): indexdata = '' self._initempty = True try: with self._indexfp() as f: if (mmapindexthreshold is not None and -self.opener.fstat(f).st_size >= mmapindexthreshold): +self.opener.fstat(f).st_size >= mmapindexthreshold): indexdata = util.buffer(util.mmapread(f)) else: indexdata = f.read() To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4866: revlog: rename _cache to _revisioncache
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY "cache" is generic and revlog instances have multiple caches. Let's be descriptive about what this is a cache for. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4866 AFFECTED FILES mercurial/bundlerepo.py mercurial/revlog.py mercurial/unionrepo.py CHANGE DETAILS diff --git a/mercurial/unionrepo.py b/mercurial/unionrepo.py --- a/mercurial/unionrepo.py +++ b/mercurial/unionrepo.py @@ -110,7 +110,7 @@ if rev > self.repotiprev: text = self.revlog2.revision(node) -self._cache = (node, rev, text) +self._revisioncache = (node, rev, text) else: text = self.baserevision(rev) # already cached diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -346,7 +346,7 @@ self._checkambig = checkambig self._censorable = censorable # 3-tuple of (node, rev, text) for a raw revision. -self._cache = None +self._revisioncache = None # Maps rev to chain base rev. self._chainbasecache = util.lrucachedict(100) # 2-tuple of (offset, data) of raw data from the revlog at an offset. @@ -545,7 +545,7 @@ return True def clearcaches(self): -self._cache = None +self._revisioncache = None self._chainbasecache.clear() self._chunkcache = (0, '') self._pcache = {} @@ -1524,35 +1524,35 @@ rawtext = None if node == nullid: return "" -if self._cache: -if self._cache[0] == node: +if self._revisioncache: +if self._revisioncache[0] == node: # _cache only stores rawtext if raw: -return self._cache[2] +return self._revisioncache[2] # duplicated, but good for perf if rev is None: rev = self.rev(node) if flags is None: flags = self.flags(rev) # no extra flags set, no flag processor runs, text = rawtext if flags == REVIDX_DEFAULT_FLAGS: -return self._cache[2] +return self._revisioncache[2] # rawtext is reusable. need to run flag processor -rawtext = self._cache[2] +rawtext = self._revisioncache[2] -cachedrev = self._cache[1] +cachedrev = self._revisioncache[1] # look up what we need to read if rawtext is None: if rev is None: rev = self.rev(node) chain, stopped = self._deltachain(rev, stoprev=cachedrev) if stopped: -rawtext = self._cache[2] +rawtext = self._revisioncache[2] # drop cache to save memory -self._cache = None +self._revisioncache = None targetsize = None rawsize = self.index[rev][2] @@ -1565,7 +1565,7 @@ bins = bins[1:] rawtext = mdiff.patches(rawtext, bins) -self._cache = (node, rev, rawtext) +self._revisioncache = (node, rev, rawtext) if flags is None: if rev is None: @@ -1926,7 +1926,7 @@ rawtext = deltacomputer.buildtext(revinfo, fh) if type(rawtext) == bytes: # only accept immutable objects -self._cache = (node, curr, rawtext) +self._revisioncache = (node, curr, rawtext) self._chainbasecache[curr] = deltainfo.chainbase return node @@ -2132,7 +2132,7 @@ transaction.add(self.indexfile, end) # then reset internal state in memory to forget those revisions -self._cache = None +self._revisioncache = None self._chaininfocache = {} self._chunkclear() for x in pycompat.xrange(rev, len(self)): diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -127,8 +127,8 @@ iterrev = rev # reconstruct the revision if it is from a changegroup while iterrev > self.repotiprev: -if self._cache and self._cache[1] == iterrev: -rawtext = self._cache[2] +if self._revisioncache and self._revisioncache[1] == iterrev: +rawtext = self._revisioncache[2] break chain.append(iterrev) iterrev = self.index[iterrev][3] @@ -143,7 +143,7 @@ 'read', raw=raw) if validatehash: self.checkhash(text, node, rev=rev) -self._cache = (node, rev, rawtext) +self._revisioncache = (node, rev, rawtext)
D4865: testing: add file storage integration for bad hashes and censoring
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY In order to implement these tests, we need a backdoor to write data into storage backends while bypassing normal checks. We invent a callable to do that. As part of writing the tests, I found a bug with censorrevision() pretty quickly! After calling censorrevision(), attempting to access revision data for an affected node raises a cryptic error related to malformed compression. This appears to be due to the revlog not adjusting delta chains as part of censoring. I also found a bug with regards to hash verification and revision fulltext caching. Essentially, we cache the fulltext before hash verification. If we look up the fulltext after a failed hash verification, we don't get a hash verification exception. Furthermore, the behavior of revision(raw=True) can be inconsistent depending on the order of operations. I'll be fixing both these bugs in subsequent commits. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4865 AFFECTED FILES mercurial/testing/storage.py tests/test-storage.py CHANGE DETAILS diff --git a/tests/test-storage.py b/tests/test-storage.py --- a/tests/test-storage.py +++ b/tests/test-storage.py @@ -5,7 +5,9 @@ import silenttestrunner from mercurial import ( +error, filelog, +revlog, transaction, ui as uimod, vfs as vfsmod, @@ -33,14 +35,39 @@ return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap, b'journal', b'undo') +def addrawrevision(self, fl, tr, node, p1, p2, linkrev, rawtext=None, + delta=None, censored=False, ellipsis=False, extstored=False): +flags = 0 + +if censored: +flags |= revlog.REVIDX_ISCENSORED +if ellipsis: +flags |= revlog.REVIDX_ELLIPSIS +if extstored: +flags |= revlog.REVIDX_EXTSTORED + +if rawtext is not None: +fl._revlog.addrawrevision(rawtext, tr, linkrev, p1, p2, node, flags) +elif delta is not None: +raise error.Abort('support for storing raw deltas not yet supported') +else: +raise error.Abort('must supply rawtext or delta arguments') + +# We may insert bad data. Clear caches to prevent e.g. cache hits to +# bypass hash verification. +fl._revlog.clearcaches() + # Assigning module-level attributes that inherit from unittest.TestCase # is all that is needed to register tests. filelogindextests = storagetesting.makeifileindextests(makefilefn, - maketransaction) + maketransaction, + addrawrevision) filelogdatatests = storagetesting.makeifiledatatests(makefilefn, - maketransaction) + maketransaction, + addrawrevision) filelogmutationtests = storagetesting.makeifilemutationtests(makefilefn, - maketransaction) + maketransaction, + addrawrevision) if __name__ == '__main__': silenttestrunner.main(__name__) diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -861,27 +861,157 @@ self.assertFalse(f.cmp(node1, fulltext1)) self.assertTrue(f.cmp(node1, stored0)) +def testbadnoderead(self): +f = self._makefilefn() + +fulltext0 = b'foo\n' * 30 +fulltext1 = fulltext0 + b'bar\n' + +with self._maketransactionfn() as tr: +node0 = f.add(fulltext0, None, tr, 0, nullid, nullid) +node1 = b'\xaa' * 20 + +self._addrawrevisionfn(f, tr, node1, node0, nullid, 1, + rawtext=fulltext1) + +self.assertEqual(len(f), 2) +self.assertEqual(f.parents(node1), (node0, nullid)) + +# revision() raises since it performs hash verification. +with self.assertRaises(error.StorageError): +f.revision(node1) + +# revision(raw=True) still verifies hashes. +# TODO this is buggy because of cache interaction. +self.assertEqual(f.revision(node1, raw=True), fulltext1) + +# read() behaves like revision(). +# TODO this is buggy because of cache interaction. +f.read(node1) + +# We can't test renamed() here because some backends may not require +# reading/validating the fulltext to return rename metadata. + +def testbadnoderevisionraw(self): +# Like above except we test revision(raw=True)
D4864: testing: add file storage tests for getstrippoint() and strip()
indygreg 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/D4864 AFFECTED FILES mercurial/testing/storage.py CHANGE DETAILS diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -1004,6 +1004,123 @@ self.assertEqual(f.node(1), nodes[1]) self.assertEqual(f.node(2), nodes[2]) +def testgetstrippointnoparents(self): +# N revisions where none have parents. +f = self._makefilefn() + +with self._maketransactionfn() as tr: +for rev in range(10): +f.add(b'%d' % rev, None, tr, rev, nullid, nullid) + +for rev in range(10): +self.assertEqual(f.getstrippoint(rev), (rev, set())) + +def testgetstrippointlinear(self): +# N revisions in a linear chain. +f = self._makefilefn() + +with self._maketransactionfn() as tr: +p1 = nullid + +for rev in range(10): +f.add(b'%d' % rev, None, tr, rev, p1, nullid) + +for rev in range(10): +self.assertEqual(f.getstrippoint(rev), (rev, set())) + +def testgetstrippointmultipleheads(self): +f = self._makefilefn() + +with self._maketransactionfn() as tr: +node0 = f.add(b'0', None, tr, 0, nullid, nullid) +node1 = f.add(b'1', None, tr, 1, node0, nullid) +f.add(b'2', None, tr, 2, node1, nullid) +f.add(b'3', None, tr, 3, node0, nullid) +f.add(b'4', None, tr, 4, node0, nullid) + +for rev in range(5): +self.assertEqual(f.getstrippoint(rev), (rev, set())) + +def testgetstrippointearlierlinkrevs(self): +f = self._makefilefn() + +with self._maketransactionfn() as tr: +node0 = f.add(b'0', None, tr, 0, nullid, nullid) +f.add(b'1', None, tr, 10, node0, nullid) +f.add(b'2', None, tr, 5, node0, nullid) + +self.assertEqual(f.getstrippoint(0), (0, set())) +self.assertEqual(f.getstrippoint(1), (1, set())) +self.assertEqual(f.getstrippoint(2), (1, set())) +self.assertEqual(f.getstrippoint(3), (1, set())) +self.assertEqual(f.getstrippoint(4), (1, set())) +self.assertEqual(f.getstrippoint(5), (1, set())) +self.assertEqual(f.getstrippoint(6), (1, {2})) +self.assertEqual(f.getstrippoint(7), (1, {2})) +self.assertEqual(f.getstrippoint(8), (1, {2})) +self.assertEqual(f.getstrippoint(9), (1, {2})) +self.assertEqual(f.getstrippoint(10), (1, {2})) +self.assertEqual(f.getstrippoint(11), (3, set())) + +def teststripempty(self): +f = self._makefilefn() + +with self._maketransactionfn() as tr: +f.strip(0, tr) + +self.assertEqual(len(f), 0) + +def teststripall(self): +f = self._makefilefn() + +with self._maketransactionfn() as tr: +p1 = nullid +for rev in range(10): +p1 = f.add(b'%d' % rev, None, tr, rev, p1, nullid) + +self.assertEqual(len(f), 10) + +with self._maketransactionfn() as tr: +f.strip(0, tr) + +self.assertEqual(len(f), 0) + +def teststrippartial(self): +f = self._makefilefn() + +with self._maketransactionfn() as tr: +f.add(b'0', None, tr, 0, nullid, nullid) +node1 = f.add(b'1', None, tr, 5, nullid, nullid) +node2 = f.add(b'2', None, tr, 10, nullid, nullid) + +self.assertEqual(len(f), 3) + +with self._maketransactionfn() as tr: +f.strip(11, tr) + +self.assertEqual(len(f), 3) + +with self._maketransactionfn() as tr: +f.strip(10, tr) + +self.assertEqual(len(f), 2) + +with self.assertRaises(error.LookupError): +f.rev(node2) + +with self._maketransactionfn() as tr: +f.strip(6, tr) + +self.assertEqual(len(f), 2) + +with self._maketransactionfn() as tr: +f.strip(3, tr) + +self.assertEqual(len(f), 1) + +with self.assertRaises(error.LookupError): +f.rev(node1) + def makeifileindextests(makefilefn, maketransactionfn): """Create a unittest.TestCase class suitable for testing file storage. To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4863: wireprotov2: always advertise raw repo requirements
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I'm pretty sure my original thinking behind making it conditional on stream clone support was that the behavior mirrored wire protocol version 1. I don't see a compelling reason for us to not advertise the server's storage requirements. The proper way to advertise stream clone support in wireprotov2 would be to not advertise the command(s) required to perform stream clone or to advertise a separate capability denoting stream clone support. Stream clone isn't yet implemented on wireprotov2, so we can cross this bridge later. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4863 AFFECTED FILES mercurial/wireprotov2server.py CHANGE DETAILS diff --git a/mercurial/wireprotov2server.py b/mercurial/wireprotov2server.py --- a/mercurial/wireprotov2server.py +++ b/mercurial/wireprotov2server.py @@ -20,7 +20,6 @@ error, narrowspec, pycompat, -streamclone, util, wireprotoframing, wireprototypes, @@ -522,9 +521,8 @@ 'permissions': [entry.permission], } -if streamclone.allowservergeneration(repo): -caps['rawrepoformats'] = sorted(repo.requirements & -repo.supportedformats) +caps['rawrepoformats'] = sorted(repo.requirements & +repo.supportedformats) targets = getadvertisedredirecttargets(repo, proto) if targets: To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4767: exchangev2: recognize narrow patterns when pulling
indygreg planned changes to this revision. indygreg added a comment. I'll be rebasing this locally as part of future feature work. I plan to resubmit again later. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4767 To: indygreg, #hg-reviewers Cc: mjpieters, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4862: cleanup: some Yoda conditions, this patch removes
This revision was automatically updated to reflect the committed changes. Closed by commit rHGe2697acd9381: cleanup: some Yoda conditions, this patch removes (authored by martinvonz, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4862?vs=11642=11643 REVISION DETAIL https://phab.mercurial-scm.org/D4862 AFFECTED FILES contrib/revsetbenchmarks.py hgext/histedit.py hgext/patchbomb.py hgext/shelve.py mercurial/cmdutil.py mercurial/debugcommands.py mercurial/obsolete.py mercurial/templatefilters.py mercurial/util.py CHANGE DETAILS diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -333,7 +333,7 @@ return self._frombuffer(min(self._lenbuf, size)) def readline(self, *args, **kwargs): -if 1 < len(self._buffer): +if len(self._buffer) > 1: # this should not happen because both read and readline end with a # _frombuffer call that collapse it. self._buffer = [''.join(self._buffer)] @@ -348,7 +348,7 @@ size = lfi + 1 if lfi < 0: # end of file size = self._lenbuf -elif 1 < len(self._buffer): +elif len(self._buffer) > 1: # we need to take previous chunks into account size += self._lenbuf - len(self._buffer[-1]) return self._frombuffer(size) @@ -360,7 +360,7 @@ if size == 0 or not self._buffer: return '' buf = self._buffer[0] -if 1 < len(self._buffer): +if len(self._buffer) > 1: buf = ''.join(self._buffer) data = buf[:size] diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py --- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -200,7 +200,7 @@ if not m: uctext = encoding.unifromlocal(text[start:]) w = len(uctext) -while 0 < w and uctext[w - 1].isspace(): +while w > 0 and uctext[w - 1].isspace(): w -= 1 yield (encoding.unitolocal(uctext[:w]), encoding.unitolocal(uctext[w:])) diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py --- a/mercurial/obsolete.py +++ b/mercurial/obsolete.py @@ -997,13 +997,13 @@ if not isinstance(predecessors, tuple): # preserve compat with old API until all caller are migrated predecessors = (predecessors,) -if 1 < len(predecessors) and len(rel[1]) != 1: +if len(predecessors) > 1 and len(rel[1]) != 1: msg = 'Fold markers can only have 1 successors, not %d' raise error.ProgrammingError(msg % len(rel[1])) for prec in predecessors: sucs = rel[1] localmetadata = metadata.copy() -if 2 < len(rel): +if len(rel) > 2: localmetadata.update(rel[2]) if not prec.mutable(): diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -2197,7 +2197,7 @@ fullsize[2] /= numfull semitotal = semisize[2] snaptotal = {} -if 0 < numsemi: +if numsemi > 0: semisize[2] /= numsemi for depth in snapsizedepth: snaptotal[depth] = snapsizedepth[depth][2] diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -861,7 +861,7 @@ if isinstance(ctxorbool, bool): if ctxorbool: return baseformname + ".merge" -elif 1 < len(ctxorbool.parents()): +elif len(ctxorbool.parents()) > 1: return baseformname + ".merge" return baseformname + ".normal" diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -302,7 +302,7 @@ hgfiles = [f for f in vfs.listdir() if f.endswith('.' + patchextension)] hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles]) -if 0 < maxbackups and maxbackups < len(hgfiles): +if maxbackups > 0 and maxbackups < len(hgfiles): bordermtime = hgfiles[-maxbackups][0] else: bordermtime = None diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py --- a/hgext/patchbomb.py +++ b/hgext/patchbomb.py @@ -187,12 +187,12 @@ elif introconfig == 'never': intro = False elif introconfig == 'auto': -intro = 1 < number +intro = number > 1 else: ui.write_err(_('warning: invalid patchbomb.intro value "%s"\n') % introconfig) ui.write_err(_('(should be one of always, never, auto)\n')) -intro = 1 < number +intro = number > 1 return intro def _formatflags(ui, repo, rev, flags): @@ -663,7 +663,7 @@ if not known[idx]:
D4862: cleanup: some Yoda conditions, this patch removes
martinvonz created this revision. Herald added a reviewer: durin42. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY It seems the factor 20 is less than the frequency of " < \d" compared to " \d > ". REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4862 AFFECTED FILES contrib/revsetbenchmarks.py hgext/histedit.py hgext/patchbomb.py hgext/shelve.py mercurial/cmdutil.py mercurial/debugcommands.py mercurial/obsolete.py mercurial/templatefilters.py mercurial/util.py CHANGE DETAILS diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -333,7 +333,7 @@ return self._frombuffer(min(self._lenbuf, size)) def readline(self, *args, **kwargs): -if 1 < len(self._buffer): +if len(self._buffer) > 1: # this should not happen because both read and readline end with a # _frombuffer call that collapse it. self._buffer = [''.join(self._buffer)] @@ -348,7 +348,7 @@ size = lfi + 1 if lfi < 0: # end of file size = self._lenbuf -elif 1 < len(self._buffer): +elif len(self._buffer) > 1: # we need to take previous chunks into account size += self._lenbuf - len(self._buffer[-1]) return self._frombuffer(size) @@ -360,7 +360,7 @@ if size == 0 or not self._buffer: return '' buf = self._buffer[0] -if 1 < len(self._buffer): +if len(self._buffer) > 1: buf = ''.join(self._buffer) data = buf[:size] diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py --- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -200,7 +200,7 @@ if not m: uctext = encoding.unifromlocal(text[start:]) w = len(uctext) -while 0 < w and uctext[w - 1].isspace(): +while w > 0 and uctext[w - 1].isspace(): w -= 1 yield (encoding.unitolocal(uctext[:w]), encoding.unitolocal(uctext[w:])) diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py --- a/mercurial/obsolete.py +++ b/mercurial/obsolete.py @@ -997,13 +997,13 @@ if not isinstance(predecessors, tuple): # preserve compat with old API until all caller are migrated predecessors = (predecessors,) -if 1 < len(predecessors) and len(rel[1]) != 1: +if len(predecessors) > 1 and len(rel[1]) != 1: msg = 'Fold markers can only have 1 successors, not %d' raise error.ProgrammingError(msg % len(rel[1])) for prec in predecessors: sucs = rel[1] localmetadata = metadata.copy() -if 2 < len(rel): +if len(rel) > 2: localmetadata.update(rel[2]) if not prec.mutable(): diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -2197,7 +2197,7 @@ fullsize[2] /= numfull semitotal = semisize[2] snaptotal = {} -if 0 < numsemi: +if numsemi > 0: semisize[2] /= numsemi for depth in snapsizedepth: snaptotal[depth] = snapsizedepth[depth][2] diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -861,7 +861,7 @@ if isinstance(ctxorbool, bool): if ctxorbool: return baseformname + ".merge" -elif 1 < len(ctxorbool.parents()): +elif len(ctxorbool.parents()) > 1: return baseformname + ".merge" return baseformname + ".normal" diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -302,7 +302,7 @@ hgfiles = [f for f in vfs.listdir() if f.endswith('.' + patchextension)] hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles]) -if 0 < maxbackups and maxbackups < len(hgfiles): +if maxbackups > 0 and maxbackups < len(hgfiles): bordermtime = hgfiles[-maxbackups][0] else: bordermtime = None diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py --- a/hgext/patchbomb.py +++ b/hgext/patchbomb.py @@ -187,12 +187,12 @@ elif introconfig == 'never': intro = False elif introconfig == 'auto': -intro = 1 < number +intro = number > 1 else: ui.write_err(_('warning: invalid patchbomb.intro value "%s"\n') % introconfig) ui.write_err(_('(should be one of always, never, auto)\n')) -intro = 1 < number +intro = number > 1 return intro def _formatflags(ui, repo, rev, flags): @@ -663,7 +663,7 @@ if not known[idx]: missing.append(h)
D4797: storageutil: implement file identifier resolution method (BC)
indygreg added a comment. In https://phab.mercurial-scm.org/D4797#73451, @martinvonz wrote: > > with the exception of partial hex node matching, we may want to consider changing revlog.lookup() as well > > Maybe even without the exception? I thought we used partial nodeids only for debug commands and maybe we can move the partial matching to a higher level. I support moving partial matching to a higher level. Although some storage backends may support a more efficient "native" partial matcher. e.g. SQLite could use various string matching functions. But I'm not sure it is worth it though: I think I'd rather have a generic object holding DAG/index data [that can be shared across all storage backends] and that exposes a partial match API. I dunno. I don't plan to do anything in this area at this time. If you want to hack on things, go for it. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4797 To: 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
mercurial@39994: 28 new changesets
28 new changesets in mercurial: https://www.mercurial-scm.org/repo/hg/rev/aab43d5861bb changeset: 39967:aab43d5861bb user:Yuya Nishihara date:Mon Sep 24 15:54:18 2018 +0900 summary: rust-chg: add project skeleton https://www.mercurial-scm.org/repo/hg/rev/86acdfe8b018 changeset: 39968:86acdfe8b018 user:Yuya Nishihara date:Mon Sep 24 15:57:28 2018 +0900 summary: rust-chg: update dependencies https://www.mercurial-scm.org/repo/hg/rev/208cb7a9d0fa changeset: 39969:208cb7a9d0fa user:Yuya Nishihara date:Mon Sep 24 16:14:35 2018 +0900 summary: rust-chg: add function to send fds via domain socket https://www.mercurial-scm.org/repo/hg/rev/a8be2cff613f changeset: 39970:a8be2cff613f user:Yuya Nishihara date:Mon Sep 24 16:22:03 2018 +0900 summary: rust-chg: add wrapper around C function https://www.mercurial-scm.org/repo/hg/rev/b1d8acd82d60 changeset: 39971:b1d8acd82d60 user:Yuya Nishihara date:Mon Sep 24 16:33:24 2018 +0900 summary: rust-chg: add parser for request messages sent to "S" channel https://www.mercurial-scm.org/repo/hg/rev/7a0ffdd4af78 changeset: 39972:7a0ffdd4af78 user:Yuya Nishihara date:Mon Sep 24 16:59:12 2018 +0900 summary: rust-chg: add future that handles "attachio" request https://www.mercurial-scm.org/repo/hg/rev/ba447b83cd56 changeset: 39973:ba447b83cd56 user:Yuya Nishihara date:Sat Sep 29 21:59:07 2018 +0900 summary: rust-chg: add low-level function to set pager fd blocking https://www.mercurial-scm.org/repo/hg/rev/a9c5fc436fd5 changeset: 39974:a9c5fc436fd5 user:Yuya Nishihara date:Mon Sep 24 18:18:35 2018 +0900 summary: rust-chg: add callback to handle pager and shell command requests https://www.mercurial-scm.org/repo/hg/rev/571d8eb39095 changeset: 39975:571d8eb39095 user:Yuya Nishihara date:Mon Sep 24 18:21:10 2018 +0900 summary: rust-chg: add state machine to handle "runcommand" request with cHg extension https://www.mercurial-scm.org/repo/hg/rev/44840bcc411a changeset: 39976:44840bcc411a user:Yuya Nishihara date:Mon Sep 24 18:33:46 2018 +0900 summary: rust-chg: port basic socket path handling from cHg of C https://www.mercurial-scm.org/repo/hg/rev/74da9d999cd7 changeset: 39977:74da9d999cd7 user:Yuya Nishihara date:Mon Sep 24 18:57:54 2018 +0900 summary: rust-chg: add Client extensions to run cHg-specific requests https://www.mercurial-scm.org/repo/hg/rev/045ea159418d changeset: 39978:045ea159418d user:Yuya Nishihara date:Mon Sep 24 19:06:30 2018 +0900 summary: rust-chg: add interface to chdir the server https://www.mercurial-scm.org/repo/hg/rev/6bdee4bc181a changeset: 39979:6bdee4bc181a user:Yuya Nishihara date:Mon Sep 24 19:23:50 2018 +0900 summary: rust-chg: add main program https://www.mercurial-scm.org/repo/hg/rev/d71e0ba34d9b changeset: 39980:d71e0ba34d9b user:Martin von Zweigbergk date:Wed Aug 08 23:17:16 2018 -0700 summary: debugcommands: add a debugindexstats command https://www.mercurial-scm.org/repo/hg/rev/da0319e024c0 changeset: 39981:da0319e024c0 user:spectral date:Tue Oct 02 13:37:12 2018 -0700 summary: treemanifests: make _loadlazy tolerate item not on _lazydirs https://www.mercurial-scm.org/repo/hg/rev/19103e68a698 changeset: 39982:19103e68a698 user:spectral date:Tue Oct 02 13:38:26 2018 -0700 summary: treemanifests: make _loadchildrensetlazy just call _loadlazy https://www.mercurial-scm.org/repo/hg/rev/3cacb74c3a22 changeset: 39983:3cacb74c3a22 user:spectral date:Tue Oct 02 13:41:00 2018 -0700 summary: treemanifests: skip extraneous check for item before calling _loadlazy https://www.mercurial-scm.org/repo/hg/rev/731961d972ba changeset: 39984:731961d972ba user:spectral date:Thu Sep 27 20:16:48 2018 -0700 summary: treemanifests: remove _loadalllazy in _diff() https://www.mercurial-scm.org/repo/hg/rev/825a636812a4 changeset: 39985:825a636812a4 user:Martin von Zweigbergk date:Tue Oct 02 09:11:18 2018 -0700 summary: narrow: avoid overwriting a variable https://www.mercurial-scm.org/repo/hg/rev/138e2d6d3b53 changeset: 39986:138e2d6d3b53 user:Matt Harbison date:Tue Oct 02 22:40:01 2018 -0400 summary: setup: ignore message about disabling 3rd party extensions because of version https://www.mercurial-scm.org/repo/hg/rev/e22016e83c1e changeset: 39987:e22016e83c1e user:Pulkit Goyal date:Wed Oct 03 13:55:51 2018 +0300 summary: manifest: remove an unused variable caught by pyflakes https://www.mercurial-scm.org/repo/hg/rev/a8ec8bce14c6 changeset: 39988:a8ec8bce14c6 user:Pulkit Goyal date:Wed Oct 03 13:59:19 2018 +0300 summary: py3: whitelist another passing tests
D4797: storageutil: implement file identifier resolution method (BC)
martinvonz added a comment. > "0" on an empty store now raises LookupError instead of returning nullid. I haven't checked the code, but I suspect this was just a special case of interpreting "rl.lookup(len(rl))" as nullid. I thought I had fixed all those cases about a month ago when I made the index not behave like that (I made only -1 look up the nullid), but maybe I missed a case here. > with the exception of partial hex node matching, we may want to consider changing revlog.lookup() as well Maybe even without the exception? I thought we used partial nodeids only for debug commands and maybe we can move the partial matching to a higher level. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4797 To: 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
D4852: localrepo: add repository feature when repo can be stream cloned
This revision was automatically updated to reflect the committed changes. Closed by commit rHG83146d176c03: localrepo: add repository feature when repo can be stream cloned (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4852?vs=11630=11640 REVISION DETAIL https://phab.mercurial-scm.org/D4852 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py CHANGE DETAILS diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -27,6 +27,8 @@ REPO_FEATURE_SHARED_STORAGE = b'sharedstore' # LFS supported for backing file storage. REPO_FEATURE_LFS = b'lfs' +# Repository supports being stream cloned. +REPO_FEATURE_STREAM_CLONE = b'streamclone' class ipeerconnection(interfaceutil.Interface): """Represents a "connection" to a repository. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -795,6 +795,7 @@ def makefilestorage(requirements, features, **kwargs): """Produce a type conforming to ``ilocalrepositoryfilestorage``.""" features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE) +features.add(repository.REPO_FEATURE_STREAM_CLONE) if repository.NARROW_REQUIREMENT in requirements: return revlognarrowfilestorage To: indygreg, #hg-reviewers, pulkit Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4853: streamclone: don't support stream clone unless repo feature present
This revision was automatically updated to reflect the committed changes. Closed by commit rHG51f10e6d66c7: streamclone: dont support stream clone unless repo feature present (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4853?vs=11631=11641 REVISION DETAIL https://phab.mercurial-scm.org/D4853 AFFECTED FILES mercurial/streamclone.py CHANGE DETAILS diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py --- a/mercurial/streamclone.py +++ b/mercurial/streamclone.py @@ -18,6 +18,7 @@ error, phases, pycompat, +repository, store, util, ) @@ -178,6 +179,9 @@ def allowservergeneration(repo): """Whether streaming clones are allowed from the server.""" +if repository.REPO_FEATURE_STREAM_CLONE not in repo.features: +return False + if not repo.ui.configbool('server', 'uncompressed', untrusted=True): return False To: indygreg, #hg-reviewers, pulkit Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4858: httppeer: report http statistics
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that keepalive.py records HTTP request count and the number of bytes sent and received as part of performing those requests, we can easily print a report on the activity when closing a peer instance! Exact byte counts are globbed in tests because they are influenced by non-deterministic things, such as hostnames and port numbers. Plus, the exact byte count isn't too important anyway. I feel obliged to note that printing the byte count could have security implications. e.g. if sending a password via HTTP basic auth, the length of that password will influence the byte count and the reporting of the byte count could be a side-channel leak of the password length. I /think/ this is beyond our threshold for concern. But if we think it poses a problem, we can teach the byte count logging code to e.g. ignore sensitive HTTP request headers. We could also consider not reporting the byte count of request headers altogether. But since the wire protocol uses HTTP headers for sending command arguments, it is kind of important to report their size. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4858 AFFECTED FILES mercurial/httppeer.py mercurial/url.py tests/test-clone-uncompressed.t tests/test-clonebundles.t tests/test-http-api-httpv2.t tests/test-http-protocol.t tests/test-lfs-serve-access.t tests/test-lfs-serve.t tests/test-schemes.t tests/test-stream-bundle-v2.t tests/test-wireproto-caching.t tests/test-wireproto-command-branchmap.t tests/test-wireproto-command-capabilities.t tests/test-wireproto-command-changesetdata.t tests/test-wireproto-command-filedata.t tests/test-wireproto-command-heads.t tests/test-wireproto-command-known.t tests/test-wireproto-command-listkeys.t tests/test-wireproto-command-lookup.t tests/test-wireproto-command-manifestdata.t tests/test-wireproto-command-pushkey.t tests/test-wireproto-content-redirects.t tests/test-wireproto-exchangev2.t CHANGE DETAILS diff --git a/tests/test-wireproto-exchangev2.t b/tests/test-wireproto-exchangev2.t --- a/tests/test-wireproto-exchangev2.t +++ b/tests/test-wireproto-exchangev2.t @@ -130,6 +130,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:caa2a465451d (3 drafts) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) All changesets should have been transferred @@ -256,6 +257,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:4432d83626e8 + (sent 6 HTTP requests and * bytes; received * bytes in responses) (glob) $ cd client-singlehead @@ -369,6 +371,7 @@ updating the branch cache new changesets cd2534766bec:caa2a465451d (3 drafts) (run 'hg update' to get a working copy) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg log -G -T '{rev} {node} {phase}\n' o 4 caa2a465451dd1facda0f5b12312c355584188a1 draft @@ -439,6 +442,7 @@ checking for updated bookmarks 2 local changesets published (run 'hg update' to get a working copy) + (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg log -G -T '{rev} {node} {phase}\n' o 4 caa2a465451dd1facda0f5b12312c355584188a1 public @@ -555,6 +559,7 @@ received frame(size=0; request=3; stream=2; streamflags=; type=command-response; flags=eos) updating the branch cache new changesets 3390ef850073:caa2a465451d (1 drafts) + (sent 5 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg -R client-bookmarks bookmarks book-10:3390ef850073 @@ -611,6 +616,7 @@ checking for updated bookmarks updating bookmark book-1 (run 'hg update' to get a working copy) + (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob) $ hg -R client-bookmarks bookmarks book-12:cd2534766bec diff --git a/tests/test-wireproto-content-redirects.t b/tests/test-wireproto-content-redirects.t --- a/tests/test-wireproto-content-redirects.t +++ b/tests/test-wireproto-content-redirects.t @@ -317,6 +317,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Unknown protocol is filtered from compatible targets @@ -610,6 +611,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) Missing SNI support filters targets that require SNI @@ -901,6 +903,7 @@ } } ] + (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob) $ cat >> $HGRCPATH << EOF > [extensions] @@ -1191,6 +1194,7 @@ } } ] + (sent 2 HTTP
D4860: repository: define and use revision flag constants
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Revlogs have a per-revision 2 byte field holding integer flags that define how revision data should be interpreted. For historical reasons, these integer values are sent verbatim on the wire protocol as part of changegroup data. From a semantic standpoint, the flags that go out over the wire are different from the flags stored internally by revlogs. Failure to establish this semantic distinction creates unwanted strong coupling between revlog's internals and the wire protocol. This commit establishes new constants on the repository module that define the revision flags used by the wire protocol (and by some internal storage APIs, sadly). The changegroups internals documentation has been updated to document them explicitly. Various references throughout the repo now use the repository constants instead of the revlog constants. This is done to make it clear that we're operating on generic revision data and this isn't tied to revlogs. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4860 AFFECTED FILES mercurial/changegroup.py mercurial/help/internals/changegroups.txt mercurial/repository.py mercurial/revlogutils/constants.py mercurial/testing/storage.py tests/simplestorerepo.py tests/test-help.t CHANGE DETAILS diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -1038,8 +1038,8 @@ There are 3 versions of changegroups: "1", "2", and "3". From a high- level, versions "1" and "2" are almost exactly the same, with the only - difference being an additional item in the *delta header*. Version "3" - adds support for revlog flags in the *delta header* and optionally + difference being an additional item in the *delta header*. Version "3" + adds support for stirage flags in the *delta header* and optionally exchanging treemanifests (enabled by setting an option on the "changegroup" part in the bundle2). @@ -1162,6 +1162,27 @@ changegroup. This allows the delta to be expressed against any parent, which can result in smaller deltas and more efficient encoding of data. + The *flags* field holds bitwise flags affecting the processing of revision + data. The following flags are defined: + + 32768 + Censored revision. The revision's fulltext has been replaced by censor + metadata. May only occur on file revisions. + + 16384 + Ellipsis revision. Revision hash does not match data (likely due to + rewritten parents). + + 8192 + Externally stored. The revision fulltext contains "key:value" "\n" + delimited metadata defining an object stored elsewhere. Used by the LFS + extension. + + For historical reasons, the integer values are identical to revlog version + 1 per-revision storage flags and correspond to bits being set in this + 2-byte field. Bits were allocated starting from the most-significant bit, + hence the reverse ordering and allocation of these flags. + Changeset Segment = @@ -3435,8 +3456,8 @@ There are 3 versions of changegroups: 1, 2, and 3. From a high-level, versions 1 and 2 are almost exactly the same, with the - only difference being an additional item in the *delta header*. Version - 3 adds support for revlog flags in the *delta header* and optionally + only difference being an additional item in the *delta header*. Version + 3 adds support for stirage flags in the *delta header* and optionally exchanging treemanifests (enabled by setting an option on the changegroup part in the bundle2). @@ -3582,6 +3603,24 @@ changegroup. This allows the delta to be expressed against any parent, which can result in smaller deltas and more efficient encoding of data. + + The *flags* field holds bitwise flags affecting the processing of revision + data. The following flags are defined: + + + 32768 + Censored revision. The revision's fulltext has been replaced by censor metadata. May only occur on file revisions. + 16384 + Ellipsis revision. Revision hash does not match data (likely due to rewritten parents). + 8192 + Externally stored. The revision fulltext contains key:value \n delimited metadata defining an object stored elsewhere. Used by the LFS extension. + + + For historical reasons, the integer values are identical to revlog version 1 + per-revision storage flags and correspond to bits being set in this 2-byte + field. Bits were allocated starting from the most-significant bit, hence the + reverse ordering and allocation of these flags. + Changeset Segment The *changeset segment* consists of a single *delta group* holding diff --git
D4859: exchangev2: add progress bar around manifest scanning
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This can take a long time on large repositories. Let's add a progress bar so we don't have long periods where it isn't obvious what is going on. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4859 AFFECTED FILES mercurial/exchangev2.py CHANGE DETAILS diff --git a/mercurial/exchangev2.py b/mercurial/exchangev2.py --- a/mercurial/exchangev2.py +++ b/mercurial/exchangev2.py @@ -320,6 +320,9 @@ ml = repo.manifestlog fnodes = collections.defaultdict(dict) +progress = repo.ui.makeprogress( +_('scanning manifests'), total=len(manifestnodes)) + for manifestnode in manifestnodes: m = ml.get(b'', manifestnode) @@ -334,6 +337,8 @@ for path, fnode in md.items(): fnodes[path].setdefault(fnode, manifestnode) +progress.increment() + return fnodes def _fetchfiles(repo, tr, remote, fnodes, linkrevs): To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4852: localrepo: add repository feature when repo can be stream cloned
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Right now, the wire protocol server assumes all repository objects can be stream cloned (unless the stream clone feature is disabled via config option). But not all storage backends or repository objects may support stream clone. This commit defines a repository feature denoting whether stream clone is supported. The feature is defined for revlog-based repositories, which should currently be "all repositories." REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4852 AFFECTED FILES mercurial/localrepo.py mercurial/repository.py CHANGE DETAILS diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -27,6 +27,8 @@ REPO_FEATURE_SHARED_STORAGE = b'sharedstore' # LFS supported for backing file storage. REPO_FEATURE_LFS = b'lfs' +# Repository supports being stream cloned. +REPO_FEATURE_STREAM_CLONE = b'streamclone' class ipeerconnection(interfaceutil.Interface): """Represents a "connection" to a repository. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -795,6 +795,7 @@ def makefilestorage(requirements, features, **kwargs): """Produce a type conforming to ``ilocalrepositoryfilestorage``.""" features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE) +features.add(repository.REPO_FEATURE_STREAM_CLONE) if repository.NARROW_REQUIREMENT in requirements: return revlognarrowfilestorage To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4857: keepalive: track number of bytes received from an HTTP response
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY We also bubble the byte count up to the HTTPConnection instance and its parent opener at read time. Unlike sending, there isn't a clear "end of response" signal we can intercept to defer updating the accounting. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4857 AFFECTED FILES mercurial/keepalive.py CHANGE DETAILS diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -392,6 +392,7 @@ method=method, **extrakw) self.fileno = sock.fileno self.code = None +self.receivedbytescount = 0 self._rbuf = '' self._rbufsize = 8096 self._handler = None # inserted by the handler later @@ -436,7 +437,16 @@ # if it's not empty. s = self._rbuf self._rbuf = '' -s += self._raw_read(amt) +data = self._raw_read(amt) + +self.receivedbytescount += len(data) +self._connection.receivedbytescount += len(data) +try: +self._handler.parent.receivedbytescount += len(data) +except AttributeError: +pass + +s += data return s # stolen from Python SVN #68532 to fix issue1088 @@ -512,6 +522,13 @@ if not new: break +self.receivedbytescount += len(new) +self._connection.receivedbytescount += len(new) +try: +self._handler.parent.receivedbytescount += len(new) +except AttributeError: +pass + chunks.append(new) i = new.find('\n') if i >= 0: @@ -557,6 +574,14 @@ return total mv = memoryview(dest) got = self._raw_readinto(mv[have:total]) + +self.receivedbytescount += got +self._connection.receivedbytescount += got +try: +self._handler.receivedbytescount += got +except AttributeError: +pass + dest[0:have] = self._rbuf got += len(self._rbuf) self._rbuf = '' @@ -643,6 +668,7 @@ def __init__(self, *args, **kwargs): httplib.HTTPConnection.__init__(self, *args, **kwargs) self.sentbytescount = 0 +self.receivedbytescount = 0 # # TEST FUNCTIONS To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4855: url: have httpsconnection inherit from our custom HTTPConnection
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This will ensure that any customizations we perform to HTTPConnection will be available to httpsconnection. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4855 AFFECTED FILES mercurial/keepalive.py mercurial/url.py CHANGE DETAILS diff --git a/mercurial/url.py b/mercurial/url.py --- a/mercurial/url.py +++ b/mercurial/url.py @@ -339,16 +339,16 @@ return logginghttpconnection(createconnection, *args, **kwargs) if has_https: -class httpsconnection(httplib.HTTPConnection): +class httpsconnection(keepalive.HTTPConnection): response_class = keepalive.HTTPResponse default_port = httplib.HTTPS_PORT # must be able to send big bundle as stream. send = _gen_sendfile(keepalive.safesend) getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection) def __init__(self, host, port=None, key_file=None, cert_file=None, *args, **kwargs): -httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs) +keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs) self.key_file = key_file self.cert_file = cert_file diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -615,6 +615,10 @@ return safegetresponse class HTTPConnection(httplib.HTTPConnection): +# url.httpsconnection inherits from this. So when adding/removing +# attributes, be sure to audit httpsconnection() for unintended +# consequences. + # use the modified response class response_class = HTTPResponse send = safesend To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4856: keepalive: track request count and bytes sent
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I want wire protocol interactions to report the number of requests made and bytes transferred. This commit teaches the very low-level custom HTTPConnection class to track the number of bytes sent to the socket. This may vary from the number of bytes that go on the wire due to e.g. TLS. That's OK. KeepAliveHandler is taught to track the total number of requests and total number of bytes sent across all requests. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4856 AFFECTED FILES mercurial/keepalive.py CHANGE DETAILS diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -174,6 +174,8 @@ class KeepAliveHandler(object): def __init__(self): self._cm = ConnectionManager() +self.requestscount = 0 +self.sentbytescount = 0 Connection Management def open_connections(self): @@ -312,6 +314,8 @@ return r def _start_transaction(self, h, req): +oldbytescount = h.sentbytescount + # What follows mostly reimplements HTTPConnection.request() # except it adds self.parent.addheaders in the mix and sends headers # in a deterministic order (to make testing easier). @@ -346,6 +350,16 @@ if urllibcompat.hasdata(req): h.send(data) +# This will fail to record events in case of I/O failure. That's OK. +self.requestscount += 1 +self.sentbytescount += h.sentbytescount - oldbytescount + +try: +self.parent.requestscount += 1 +self.parent.sentbytescount += h.sentbytescount - oldbytescount +except AttributeError: +pass + class HTTPHandler(KeepAliveHandler, urlreq.httphandler): pass @@ -585,9 +599,11 @@ data = read(blocksize) while data: self.sock.sendall(data) +self.sentbytescount += len(data) data = read(blocksize) else: self.sock.sendall(str) +self.sentbytescount += len(str) except socket.error as v: reraise = True if v[0] == errno.EPIPE: # Broken pipe @@ -624,6 +640,9 @@ send = safesend getresponse = wrapgetresponse(httplib.HTTPConnection) +def __init__(self, *args, **kwargs): +httplib.HTTPConnection.__init__(self, *args, **kwargs) +self.sentbytescount = 0 # # TEST FUNCTIONS To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4854: cborutil: change buffering strategy
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Profiling revealed that we were spending a lot of time on the line that was concatenating the old buffer with the incoming data when attempting to decode long byte strings, such as manifest revisions. Essentially, we were feeding N chunks of size len(X) << len(Y) into decode() and continuously allocating a new, larger buffer to hold the undecoded input. This created substantial memory churn and slowed down execution. Changing the code to aggregate pending chunks in a list until we have enough data to fully decode the next atom makes things much more efficient. I don't have exact data, but I recall the old code spending >1s on manifest fulltexts from the mozilla-unified repo. The new code doesn't significantly appear in profile output. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4854 AFFECTED FILES mercurial/utils/cborutil.py CHANGE DETAILS diff --git a/mercurial/utils/cborutil.py b/mercurial/utils/cborutil.py --- a/mercurial/utils/cborutil.py +++ b/mercurial/utils/cborutil.py @@ -913,7 +913,8 @@ """ def __init__(self): self._decoder = sansiodecoder() -self._leftover = None +self._chunks = [] +self._wanted = 0 def decode(self, b): """Attempt to decode bytes to CBOR values. @@ -924,19 +925,40 @@ * Integer number of bytes decoded from the new input. * Integer number of bytes wanted to decode the next value. """ +# Our strategy for buffering is to aggregate the incoming chunks in a +# list until we've received enough data to decode the next item. +# This is slightly more complicated than using an ``io.BytesIO`` +# or continuously concatenating incoming data. However, because it +# isn't constantly reallocating backing memory for a growing buffer, +# it prevents excessive memory thrashing and is significantly faster, +# especially in cases where the percentage of input chunks that don't +# decode into a full item is high. -if self._leftover: -oldlen = len(self._leftover) -b = self._leftover + b -self._leftover = None +if self._chunks: +# A previous call said we needed N bytes to decode the next item. +# But this call doesn't provide enough data. We buffer the incoming +# chunk without attempting to decode. +if len(b) < self._wanted: +self._chunks.append(b) +self._wanted -= len(b) +return False, 0, self._wanted + +# Else we may have enough data to decode the next item. Aggregate +# old data with new and reset the buffer. +newlen = len(b) +self._chunks.append(b) +b = b''.join(self._chunks) +self._chunks = [] +oldlen = len(b) - newlen + else: -b = b oldlen = 0 available, readcount, wanted = self._decoder.decode(b) +self._wanted = wanted if readcount < len(b): -self._leftover = b[readcount:] +self._chunks.append(b[readcount:]) return available, readcount - oldlen, wanted To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4853: streamclone: don't support stream clone unless repo feature present
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This change means custom repository types must opt in to enabling stream clone. This seems reasonable, as stream clones are a very low-level feature that has historically assumed the use of revlogs and the layout of .hg/ that they entail. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4853 AFFECTED FILES mercurial/streamclone.py CHANGE DETAILS diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py --- a/mercurial/streamclone.py +++ b/mercurial/streamclone.py @@ -18,6 +18,7 @@ error, phases, pycompat, +repository, store, util, ) @@ -178,6 +179,9 @@ def allowservergeneration(repo): """Whether streaming clones are allowed from the server.""" +if repository.REPO_FEATURE_STREAM_CLONE not in repo.features: +return False + if not repo.ui.configbool('server', 'uncompressed', untrusted=True): return False To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2679: [PoC] obsolete: config option to enable local only obsolescence mode
martinvonz added a comment. Here's a case I just ran into where I would have liked to de-obsolete a commit: 1. Someone queues a patch and pushes to central repo. Repo looks like this: o B | o A 2. I fix a typo and and amend, but I forget to push. 3. I build my own commit on top. My repo: o X | o B' | o A 4. Someone pushes new patches to central repo. Central repo: o C | o B | o A 5. I pull from central repo. My repo: o C | x B | | o X | | | o B' |/ o A 6. I move my change onto the new upstream and prune my local B' that's now unwanted: o X' | o C | x B | | x B' |/ o A If upstream history was really just a single commit (`C` above), then the obvious thing to do is to just evolve that commit and push it. However, sometimes there is a long chain on top, and in my case there was a separate commit upstream that fixed the typo I fixed when I amended `B` and it would be a little confusing to explain why someone's fix was "lost". So I'd prefer to mark B as no longer obsolete. I know I can do that with `hg strip B'`, but that also strips `X`, which is probably not a big loss, but it's a weird side-effect. Perhaps all I'm asking for is a way of dropping the obsmarker between `B` and `B'`. I don't know what the best UI for that would be, though. For now, I guess I'll use `hg strip`. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2679 To: indygreg, #hg-reviewers Cc: martinvonz, markand, durin42, lothiraldan, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4803: storageutil: extract most of emitrevisions() to standalone function
indygreg added a comment. FWIW I would like to rewrite all of filelog (and this API) to be in terms of nodes. My hands are somewhat tied with this function due to how all the functions are implemented in terms of revs today. We'll get there eventually... REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4803 To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4777: wireprotov2: server support for sending content redirects
This revision was automatically updated to reflect the committed changes. Closed by commit rHGb099e6032f38: wireprotov2: server support for sending content redirects (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4777?vs=11448=11628 REVISION DETAIL https://phab.mercurial-scm.org/D4777 AFFECTED FILES mercurial/wireprotoframing.py mercurial/wireprototypes.py mercurial/wireprotov2server.py tests/test-http-api-httpv2.t tests/test-wireproto-content-redirects.t tests/test-wireproto-serverreactor.py tests/wireprotosimplecache.py CHANGE DETAILS diff --git a/tests/wireprotosimplecache.py b/tests/wireprotosimplecache.py --- a/tests/wireprotosimplecache.py +++ b/tests/wireprotosimplecache.py @@ -12,6 +12,7 @@ registrar, repository, util, +wireprotoserver, wireprototypes, wireprotov2server, ) @@ -25,18 +26,59 @@ configtable = {} configitem = registrar.configitem(configtable) +configitem('simplecache', 'cacheapi', + default=False) configitem('simplecache', 'cacheobjects', default=False) configitem('simplecache', 'redirectsfile', default=None) +# API handler that makes cached keys available. +def handlecacherequest(rctx, req, res, checkperm, urlparts): +if rctx.repo.ui.configbool('simplecache', 'cacheobjects'): +res.status = b'500 Internal Server Error' +res.setbodybytes(b'cacheobjects not supported for api server') +return + +if not urlparts: +res.status = b'200 OK' +res.headers[b'Content-Type'] = b'text/plain' +res.setbodybytes(b'simple cache server') +return + +key = b'/'.join(urlparts) + +if key not in CACHE: +res.status = b'404 Not Found' +res.headers[b'Content-Type'] = b'text/plain' +res.setbodybytes(b'key not found in cache') +return + +res.status = b'200 OK' +res.headers[b'Content-Type'] = b'application/mercurial-cbor' +res.setbodybytes(CACHE[key]) + +def cachedescriptor(req, repo): +return {} + +wireprotoserver.API_HANDLERS[b'simplecache'] = { +'config': (b'simplecache', b'cacheapi'), +'handler': handlecacherequest, +'apidescriptor': cachedescriptor, +} + @interfaceutil.implementer(repository.iwireprotocolcommandcacher) class memorycacher(object): -def __init__(self, ui, command, encodefn): +def __init__(self, ui, command, encodefn, redirecttargets, redirecthashes, + req): self.ui = ui self.encodefn = encodefn +self.redirecttargets = redirecttargets +self.redirecthashes = redirecthashes +self.req = req self.key = None self.cacheobjects = ui.configbool('simplecache', 'cacheobjects') +self.cacheapi = ui.configbool('simplecache', 'cacheapi') self.buffered = [] ui.log('simplecache', 'cacher constructed for %s\n', command) @@ -65,6 +107,37 @@ entry = CACHE[self.key] self.ui.log('simplecache', 'cache hit for %s\n', self.key) +redirectable = True + +if not self.cacheapi: +redirectable = False +elif not self.redirecttargets: +redirectable = False +else: +clienttargets = set(self.redirecttargets) +ourtargets = set(t[b'name'] for t in loadredirecttargets(self.ui)) + +# We only ever redirect to a single target (for now). So we don't +# need to store which target matched. +if not clienttargets & ourtargets: +redirectable = False + +if redirectable: +paths = self.req.dispatchparts[:-3] +paths.append(b'simplecache') +paths.append(self.key) + +url = b'%s/%s' % (self.req.advertisedbaseurl, b'/'.join(paths)) + +#url = b'http://example.com/%s' % self.key +self.ui.log('simplecache', 'sending content redirect for %s to ' + '%s\n', self.key, url) +response = wireprototypes.alternatelocationresponse( +url=url, +mediatype=b'application/mercurial-cbor') + +return {'objs': [response]} + if self.cacheobjects: return { 'objs': entry, @@ -91,8 +164,10 @@ return [] -def makeresponsecacher(orig, repo, proto, command, args, objencoderfn): -return memorycacher(repo.ui, command, objencoderfn) +def makeresponsecacher(orig, repo, proto, command, args, objencoderfn, + redirecttargets, redirecthashes): +return memorycacher(repo.ui, command, objencoderfn, redirecttargets, +redirecthashes, proto._req) def loadredirecttargets(ui): path = ui.config('simplecache', 'redirectsfile') diff --git a/tests/test-wireproto-serverreactor.py b/tests/test-wireproto-serverreactor.py ---
D4778: wireprotov2: client support for following content redirects
This revision was automatically updated to reflect the committed changes. Closed by commit rHG7e807b8a9e56: wireprotov2: client support for following content redirects (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4778?vs=11449=11629 REVISION DETAIL https://phab.mercurial-scm.org/D4778 AFFECTED FILES mercurial/httppeer.py mercurial/wireprotov2peer.py tests/test-wireproto-content-redirects.t CHANGE DETAILS diff --git a/tests/test-wireproto-content-redirects.t b/tests/test-wireproto-content-redirects.t --- a/tests/test-wireproto-content-redirects.t +++ b/tests/test-wireproto-content-redirects.t @@ -1354,8 +1354,33 @@ s> 0\r\n s> \r\n received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos) - abort: redirect responses not yet supported - [255] + (following redirect to http://*:$HGPORT/api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0) (glob) + s> GET /api/simplecache/c045a581599d58608efd3d93d8129841f2af04a0 HTTP/1.1\r\n + s> Accept-Encoding: identity\r\n + s> accept: application/mercurial-cbor\r\n + s> host: *:$HGPORT\r\n (glob) + s> user-agent: Mercurial debugwireproto\r\n + s> \r\n + s> makefile('rb', None) + s> HTTP/1.1 200 OK\r\n + s> Server: testing stub value\r\n + s> Date: $HTTP_DATE$\r\n + s> Content-Type: application/mercurial-cbor\r\n + s> Content-Length: 91\r\n + s> \r\n + s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 + response: gen[ +{ + b'totalitems': 1 +}, +{ + b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN', + b'parents': [ + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + ] +} + ] $ cat error.log $ killdaemons.py diff --git a/mercurial/wireprotov2peer.py b/mercurial/wireprotov2peer.py --- a/mercurial/wireprotov2peer.py +++ b/mercurial/wireprotov2peer.py @@ -13,8 +13,12 @@ from . import ( encoding, error, +pycompat, sslutil, +url as urlmod, +util, wireprotoframing, +wireprototypes, ) from .utils import ( cborutil, @@ -112,9 +116,10 @@ events occur. """ -def __init__(self, requestid, command): +def __init__(self, requestid, command, fromredirect=False): self.requestid = requestid self.command = command +self.fromredirect = fromredirect # Whether all remote input related to this command has been # received. @@ -132,6 +137,7 @@ self._pendingevents = [] self._decoder = cborutil.bufferingdecoder() self._seeninitial = False +self._redirect = None def _oninputcomplete(self): with self._lock: @@ -146,10 +152,19 @@ with self._lock: for o in self._decoder.getavailable(): -if not self._seeninitial: +if not self._seeninitial and not self.fromredirect: self._handleinitial(o) continue +# We should never see an object after a content redirect, +# as the spec says the main status object containing the +# content redirect is the only object in the stream. Fail +# if we see a misbehaving server. +if self._redirect: +raise error.Abort(_('received unexpected response data ' +'after content redirect; the remote is ' +'buggy')) + self._pendingevents.append(o) self._serviceable.set() @@ -160,7 +175,16 @@ return elif o[b'status'] == b'redirect': -raise error.Abort(_('redirect responses not yet supported')) +l = o[b'location'] +self._redirect = wireprototypes.alternatelocationresponse( +url=l[b'url'], +mediatype=l[b'mediatype'], +size=l.get(b'size'), +fullhashes=l.get(b'fullhashes'), +fullhashseed=l.get(b'fullhashseed'), +serverdercerts=l.get(b'serverdercerts'), +servercadercerts=l.get(b'servercadercerts')) +return atoms = [{'msg': o[b'error'][b'message']}] if b'args' in o[b'error']: @@ -214,13 +238,17 @@ with the higher-level peer API. """ -def __init__(self, ui, clientreactor): +def __init__(self, ui, clientreactor, opener=None, + requestbuilder=util.urlreq.request): self._ui = ui
D4776: wireprotov2: client support for advertising redirect targets
This revision was automatically updated to reflect the committed changes. Closed by commit rHG86b22a4cfab1: wireprotov2: client support for advertising redirect targets (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4776?vs=11447=11627 REVISION DETAIL https://phab.mercurial-scm.org/D4776 AFFECTED FILES mercurial/httppeer.py mercurial/wireprotoframing.py mercurial/wireprotov2peer.py tests/test-wireproto-clientreactor.py tests/test-wireproto-content-redirects.t CHANGE DETAILS diff --git a/tests/test-wireproto-content-redirects.t b/tests/test-wireproto-content-redirects.t --- a/tests/test-wireproto-content-redirects.t +++ b/tests/test-wireproto-content-redirects.t @@ -57,16 +57,17 @@ s> Content-Length: 1970\r\n s> \r\n s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa6Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x82\xa1DnameDzstd\xa1DnameDzlibQframingmediatypes\x81X/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa5DnameHtarget-aHprotocolDhttpKsnirequired\xf4Ktlsversions\x82C1.2C1.3Duris\x81Shttp://example.com/Nv1capabilitiesY\x01\xd8batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + (remote redirect target target-a is compatible) sending capabilities command s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n s> Accept-Encoding: identity\r\n s> accept: application/mercurial-exp-framing-0005\r\n s> content-type: application/mercurial-exp-framing-0005\r\n - s> content-length: 27\r\n + s> content-length: 75\r\n s> host: $LOCALIP:$HGPORT\r\n (glob) s> user-agent: Mercurial debugwireproto\r\n s> \r\n - s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities + s> C\x00\x00\x01\x00\x01\x01\x11\xa2DnameLcapabilitiesHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81Htarget-a s> makefile('rb', None) s> HTTP/1.1 200 OK\r\n s> Server: testing stub value\r\n @@ -308,6 +309,8 @@ } ] +Unknown protocol is filtered from compatible targets + $ cat > redirects.py << EOF > [ > { @@ -344,16 +347,18 @@ s> Content-Length: 1997\r\n s> \r\n s>
D4775: wireprotov2: advertise redirect targets in capabilities
This revision was automatically updated to reflect the committed changes. Closed by commit rHG10cf8b116dd8: wireprotov2: advertise redirect targets in capabilities (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4775?vs=11446=11626 REVISION DETAIL https://phab.mercurial-scm.org/D4775 AFFECTED FILES mercurial/wireprotov2server.py tests/test-wireproto-content-redirects.t tests/wireprotosimplecache.py CHANGE DETAILS diff --git a/tests/wireprotosimplecache.py b/tests/wireprotosimplecache.py --- a/tests/wireprotosimplecache.py +++ b/tests/wireprotosimplecache.py @@ -17,6 +17,7 @@ ) from mercurial.utils import ( interfaceutil, +stringutil, ) CACHE = None @@ -26,6 +27,8 @@ configitem('simplecache', 'cacheobjects', default=False) +configitem('simplecache', 'redirectsfile', + default=None) @interfaceutil.implementer(repository.iwireprotocolcommandcacher) class memorycacher(object): @@ -91,10 +94,25 @@ def makeresponsecacher(orig, repo, proto, command, args, objencoderfn): return memorycacher(repo.ui, command, objencoderfn) +def loadredirecttargets(ui): +path = ui.config('simplecache', 'redirectsfile') +if not path: +return [] + +with open(path, 'rb') as fh: +s = fh.read() + +return stringutil.evalpythonliteral(s) + +def getadvertisedredirecttargets(orig, repo, proto): +return loadredirecttargets(repo.ui) + def extsetup(ui): global CACHE CACHE = util.lrucachedict(1) extensions.wrapfunction(wireprotov2server, 'makeresponsecacher', makeresponsecacher) +extensions.wrapfunction(wireprotov2server, 'getadvertisedredirecttargets', +getadvertisedredirecttargets) diff --git a/tests/test-wireproto-content-redirects.t b/tests/test-wireproto-content-redirects.t new file mode 100644 --- /dev/null +++ b/tests/test-wireproto-content-redirects.t @@ -0,0 +1,601 @@ + $ . $TESTDIR/wireprotohelpers.sh + + $ hg init server + $ enablehttpv2 server + $ cd server + $ cat >> .hg/hgrc << EOF + > [extensions] + > simplecache = $TESTDIR/wireprotosimplecache.py + > EOF + + $ echo a0 > a + $ echo b0 > b + $ hg -q commit -A -m 'commit 0' + $ echo a1 > a + $ hg commit -m 'commit 1' + + $ hg --debug debugindex -m + rev linkrev nodeid p1 p2 + 0 0 992f4779029a3df8d0666d00bb924f69634e2641 + 1 1 a988fb43583e871d1ed5750ee074c6d840bbbfc8 992f4779029a3df8d0666d00bb924f69634e2641 + + $ hg --config simplecache.redirectsfile=redirects.py serve -p $HGPORT -d --pid-file hg.pid -E error.log + $ cat hg.pid > $DAEMON_PIDS + + $ cat > redirects.py << EOF + > [ + > { + > b'name': b'target-a', + > b'protocol': b'http', + > b'snirequired': False, + > b'tlsversions': [b'1.2', b'1.3'], + > b'uris': [b'http://example.com/'], + > }, + > ] + > EOF + +Redirect targets advertised when configured + + $ sendhttpv2peerhandshake << EOF + > command capabilities + > EOF + creating http peer for wire protocol version 2 + s> GET /?cmd=capabilities HTTP/1.1\r\n + s> Accept-Encoding: identity\r\n + s> vary: X-HgProto-1,X-HgUpgrade-1\r\n + s> x-hgproto-1: cbor\r\n + s> x-hgupgrade-1: exp-http-v2-0002\r\n + s> accept: application/mercurial-0.1\r\n + s> host: $LOCALIP:$HGPORT\r\n (glob) + s> user-agent: Mercurial debugwireproto\r\n + s> \r\n + s> makefile('rb', None) + s> HTTP/1.1 200 OK\r\n + s> Server: testing stub value\r\n + s> Date: $HTTP_DATE$\r\n + s> Content-Type: application/mercurial-cbor\r\n + s> Content-Length: 1970\r\n + s> \r\n + s>
D4773: wireprotov2: support response caching
This revision was automatically updated to reflect the committed changes. Closed by commit rHGc537144fdbef: wireprotov2: support response caching (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4773?vs=11444=11624 REVISION DETAIL https://phab.mercurial-scm.org/D4773 AFFECTED FILES mercurial/repository.py mercurial/wireprototypes.py mercurial/wireprotov2server.py tests/test-wireproto-caching.t tests/wireprotosimplecache.py CHANGE DETAILS diff --git a/tests/wireprotosimplecache.py b/tests/wireprotosimplecache.py new file mode 100644 --- /dev/null +++ b/tests/wireprotosimplecache.py @@ -0,0 +1,100 @@ +# wireprotosimplecache.py - Extension providing in-memory wire protocol cache +# +# Copyright 2018 Gregory Szorc +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from mercurial import ( +extensions, +registrar, +repository, +util, +wireprototypes, +wireprotov2server, +) +from mercurial.utils import ( +interfaceutil, +) + +CACHE = None + +configtable = {} +configitem = registrar.configitem(configtable) + +configitem('simplecache', 'cacheobjects', + default=False) + +@interfaceutil.implementer(repository.iwireprotocolcommandcacher) +class memorycacher(object): +def __init__(self, ui, command, encodefn): +self.ui = ui +self.encodefn = encodefn +self.key = None +self.cacheobjects = ui.configbool('simplecache', 'cacheobjects') +self.buffered = [] + +ui.log('simplecache', 'cacher constructed for %s\n', command) + +def __enter__(self): +return self + +def __exit__(self, exctype, excvalue, exctb): +if exctype: +self.ui.log('simplecache', 'cacher exiting due to error\n') + +def adjustcachekeystate(self, state): +# Needed in order to make tests deterministic. Don't copy this +# pattern for production caches! +del state[b'repo'] + +def setcachekey(self, key): +self.key = key +return True + +def lookup(self): +if self.key not in CACHE: +self.ui.log('simplecache', 'cache miss for %s\n', self.key) +return None + +entry = CACHE[self.key] +self.ui.log('simplecache', 'cache hit for %s\n', self.key) + +if self.cacheobjects: +return { +'objs': entry, +} +else: +return { +'objs': [wireprototypes.encodedresponse(entry)], +} + +def onobject(self, obj): +if self.cacheobjects: +self.buffered.append(obj) +else: +self.buffered.extend(self.encodefn(obj)) + +yield obj + +def onfinished(self): +self.ui.log('simplecache', 'storing cache entry for %s\n', self.key) +if self.cacheobjects: +CACHE[self.key] = self.buffered +else: +CACHE[self.key] = b''.join(self.buffered) + +return [] + +def makeresponsecacher(orig, repo, proto, command, args, objencoderfn): +return memorycacher(repo.ui, command, objencoderfn) + +def extsetup(ui): +global CACHE + +CACHE = util.lrucachedict(1) + +extensions.wrapfunction(wireprotov2server, 'makeresponsecacher', +makeresponsecacher) diff --git a/tests/test-wireproto-caching.t b/tests/test-wireproto-caching.t new file mode 100644 --- /dev/null +++ b/tests/test-wireproto-caching.t @@ -0,0 +1,645 @@ + $ . $TESTDIR/wireprotohelpers.sh + $ cat >> $HGRCPATH << EOF + > [extensions] + > blackbox = + > [blackbox] + > track = simplecache + > EOF + $ hg init server + $ enablehttpv2 server + $ cd server + $ cat >> .hg/hgrc << EOF + > [extensions] + > simplecache = $TESTDIR/wireprotosimplecache.py + > EOF + + $ echo a0 > a + $ echo b0 > b + $ hg -q commit -A -m 'commit 0' + $ echo a1 > a + $ hg commit -m 'commit 1' + $ echo b1 > b + $ hg commit -m 'commit 2' + $ echo a2 > a + $ echo b2 > b + $ hg commit -m 'commit 3' + + $ hg log -G -T '{rev}:{node} {desc}' + @ 3:50590a86f3ff5d1e9a1624a7a6957884565cc8e8 commit 3 + | + o 2:4d01eda50c6ac5f7e89cbe1880143a32f559c302 commit 2 + | + o 1:4432d83626e8a98655f062ec1f2a43b07f7fbbb0 commit 1 + | + o 0:3390ef850073fbc2f0dfff2244342c8e9229013a commit 0 + + + $ hg --debug debugindex -m + rev linkrev nodeid p1 p2 + 0 0 992f4779029a3df8d0666d00bb924f69634e2641 + 1 1 a988fb43583e871d1ed5750ee074c6d840bbbfc8 992f4779029a3df8d0666d00bb924f69634e2641 + 2 2 a8853dafacfca6fc807055a660d8b835141a3bb4
D4769: debugcommands: print all CBOR objects
This revision was automatically updated to reflect the committed changes. Closed by commit rHG426cb2859013: debugcommands: print all CBOR objects (authored by indygreg, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D4769?vs=11440=11620#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4769?vs=11440=11620 REVISION DETAIL https://phab.mercurial-scm.org/D4769 AFFECTED FILES mercurial/debugcommands.py tests/test-wireproto-command-capabilities.t CHANGE DETAILS diff --git a/tests/test-wireproto-command-capabilities.t b/tests/test-wireproto-command-capabilities.t --- a/tests/test-wireproto-command-capabilities.t +++ b/tests/test-wireproto-command-capabilities.t @@ -146,11 +146,13 @@ s> Content-Length: *\r\n (glob) s> \r\n s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash - cbor> { -b'apibase': b'api/', -b'apis': {}, -b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash' - } + cbor> [ +{ + b'apibase': b'api/', + b'apis': {}, + b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash' +} + ] Restart server to enable HTTPv2 @@ -183,11 +185,13 @@ s> Content-Length: *\r\n (glob) s> \r\n s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash - cbor> { -b'apibase': b'api/', -b'apis': {}, -b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash' - } + cbor> [ +{ + b'apibase': b'api/', + b'apis': {}, + b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash' +} + ] Request for HTTPv2 service returns information about it @@ -213,204 +217,206 @@ s> Content-Length: *\r\n (glob) s> \r\n s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1
D4774: wireprotov2: define semantics for content redirects
This revision was automatically updated to reflect the committed changes. Closed by commit rHG33eb670e2834: wireprotov2: define semantics for content redirects (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4774?vs=11445=11625 REVISION DETAIL https://phab.mercurial-scm.org/D4774 AFFECTED FILES mercurial/help/internals/wireprotocolrpc.txt mercurial/help/internals/wireprotocolv2.txt CHANGE DETAILS diff --git a/mercurial/help/internals/wireprotocolv2.txt b/mercurial/help/internals/wireprotocolv2.txt --- a/mercurial/help/internals/wireprotocolv2.txt +++ b/mercurial/help/internals/wireprotocolv2.txt @@ -111,6 +111,38 @@ requirements can be used to determine whether a client can read a *raw* copy of file data available. +redirect + A map declaring potential *content redirects* that may be used by this + server. Contains the following bytestring keys: + + targets + (array of maps) Potential redirect targets. Values are maps describing + this target in more detail. Each map has the following bytestring keys: + + name + (bytestring) Identifier for this target. The identifier will be used + by clients to uniquely identify this target. + + protocol + (bytestring) High-level network protocol. Values can be + ``http``, ```https``, ``ssh``, etc. + + uris + (array of bytestrings) Representative URIs for this target. + + snirequired (optional) + (boolean) Indicates whether Server Name Indication is required + to use this target. Defaults to False. + + tlsversions (optional) + (array of bytestring) Indicates which TLS versions are supported by + this target. Values are ``1.1``, ``1.2``, ``1.3``, etc. + + hashes + (array of bytestring) Indicates support for hashing algorithms that are + used to ensure content integrity. Values include ``sha1``, ``sha256``, + etc. + changesetdata - diff --git a/mercurial/help/internals/wireprotocolrpc.txt b/mercurial/help/internals/wireprotocolrpc.txt --- a/mercurial/help/internals/wireprotocolrpc.txt +++ b/mercurial/help/internals/wireprotocolrpc.txt @@ -115,6 +115,22 @@ Each command defines its own set of argument names and their expected types. +redirect (optional) + (map) Advertises client support for following response *redirects*. + + This map has the following bytestring keys: + + targets + (array of bytestring) List of named redirect targets supported by + this client. The names come from the targets advertised by the + server's *capabilities* message. + + hashes + (array of bytestring) List of preferred hashing algorithms that can + be used for content integrity verification. + + See the *Content Redirects* section below for more on content redirects. + This frame type MUST ONLY be sent from clients to servers: it is illegal for a server to send this frame to a client. @@ -506,6 +522,9 @@ error There was an error processing the command. More details about the error are encoded in the ``error`` key. + redirect + The response for this command is available elsewhere. Details on + where are in the ``location`` key. error (optional) A map containing information about an encountered error. The map has the @@ -515,5 +534,116 @@ (array of maps) A message describing the error. The message uses the same format as those in the ``Human Output Side-Channel`` frame. +location (optional) + (map) Presence indicates that a *content redirect* has occurred. The map + provides the external location of the content. + + This map contains the following bytestring keys: + + url + (bytestring) URL from which this content may be requested. + + mediatype + (bytestring) The media type for the fetched content. e.g. + ``application/mercurial-*``. + + In some transports, this value is also advertised by the transport. + e.g. as the ``Content-Type`` HTTP header. + + size (optional) + (unsigned integer) Total size of remote object in bytes. This is + the raw size of the entity that will be fetched, minus any + non-Mercurial protocol encoding (e.g. HTTP content or transfer + encoding.) + + fullhashes (optional) + (array of arrays) Content hashes for the entire payload. Each entry + is an array of bytestrings containing the hash name and the hash value. + + fullhashseed (optional) + (bytestring) Optional seed value to feed into hasher for full content + hash verification. + + serverdercerts (optional) + (array of bytestring) DER encoded x509 certificates for the server. When + defined, clients MAY validate that the x509 certificate on the target + server exactly matches the certificate used here. + + servercadercerts (optional) + (array of bytestring) DER encoded
D4772: wireprotov2: define type to represent pre-encoded object
This revision was automatically updated to reflect the committed changes. Closed by commit rHGed919b90acda: wireprotov2: define type to represent pre-encoded object (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4772?vs=11443=11623 REVISION DETAIL https://phab.mercurial-scm.org/D4772 AFFECTED FILES mercurial/wireprotoframing.py mercurial/wireprototypes.py CHANGE DETAILS diff --git a/mercurial/wireprototypes.py b/mercurial/wireprototypes.py --- a/mercurial/wireprototypes.py +++ b/mercurial/wireprototypes.py @@ -10,6 +10,9 @@ hex, ) from .i18n import _ +from .thirdparty import ( +attr, +) from . import ( error, util, @@ -352,3 +355,15 @@ ', '.sorted(validnames)) return compengines + +@attr.s +class encodedresponse(object): +"""Represents response data that is already content encoded. + +Wire protocol version 2 only. + +Commands typically emit Python objects that are encoded and sent over the +wire. If commands emit an object of this type, the encoding step is bypassed +and the content from this object is used instead. +""" +data = attr.ib() diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py --- a/mercurial/wireprotoframing.py +++ b/mercurial/wireprotoframing.py @@ -22,6 +22,7 @@ encoding, error, util, +wireprototypes, ) from .utils import ( cborutil, @@ -840,10 +841,22 @@ yield createcommandresponseokframe(stream, requestid) emitted = True -for chunk in cborutil.streamencode(o): -for frame in emitter.send(chunk): +# Objects emitted by command functions can be serializable +# data structures or special types. +# TODO consider extracting the content normalization to a +# standalone function, as it may be useful for e.g. cachers. + +# A pre-encoded object is sent directly to the emitter. +if isinstance(o, wireprototypes.encodedresponse): +for frame in emitter.send(o.data): yield frame +# A regular object is CBOR encoded. +else: +for chunk in cborutil.streamencode(o): +for frame in emitter.send(chunk): +yield frame + except Exception as e: for frame in createerrorframe(stream, requestid, '%s' % e, To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4771: wireprotov2: change name and behavior of readframe()
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf5a05bb48116: wireprotov2: change name and behavior of readframe() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4771?vs=11442=11622 REVISION DETAIL https://phab.mercurial-scm.org/D4771 AFFECTED FILES mercurial/httppeer.py mercurial/wireprotov2peer.py CHANGE DETAILS diff --git a/mercurial/wireprotov2peer.py b/mercurial/wireprotov2peer.py --- a/mercurial/wireprotov2peer.py +++ b/mercurial/wireprotov2peer.py @@ -148,6 +148,7 @@ self._requests = {} self._futures = {} self._responses = {} +self._frameseof = False def callcommand(self, command, args, f): """Register a request to call a command. @@ -180,18 +181,23 @@ return meta['framegen'] -def readframe(self, fh): -"""Attempt to read and process a frame. +def readdata(self, framefh): +"""Attempt to read data and do work. -Returns None if no frame was read. Presumably this means EOF. +Returns None if no data was read. Presumably this means we're +done with all read I/O. """ -frame = wireprotoframing.readframe(fh) -if frame is None: -# TODO tell reactor? -return +if not self._frameseof: +frame = wireprotoframing.readframe(framefh) +if frame is None: +# TODO tell reactor? +self._frameseof = True +else: +self._ui.note(_('received %r\n') % frame) +self._processframe(frame) -self._ui.note(_('received %r\n') % frame) -self._processframe(frame) +if self._frameseof: +return None return True diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -712,7 +712,7 @@ def _handleresponse(self, handler, resp): # Called in a thread to read the response. -while handler.readframe(resp): +while handler.readdata(resp): pass # TODO implement interface for version 2 peers To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4770: url: move _wraphttpresponse() from httpeer
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf80db6adabbe: url: move _wraphttpresponse() from httpeer (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4770?vs=11441=11621 REVISION DETAIL https://phab.mercurial-scm.org/D4770 AFFECTED FILES mercurial/httppeer.py mercurial/url.py CHANGE DETAILS diff --git a/mercurial/url.py b/mercurial/url.py --- a/mercurial/url.py +++ b/mercurial/url.py @@ -595,3 +595,39 @@ url_ = 'file://' + pycompat.bytesurl(urlreq.pathname2url(path)) authinfo = None return opener(ui, authinfo).open(pycompat.strurl(url_), data) + +def wrapresponse(resp): +"""Wrap a response object with common error handlers. + +This ensures that any I/O from any consumer raises the appropriate +error and messaging. +""" +origread = resp.read + +class readerproxy(resp.__class__): +def read(self, size=None): +try: +return origread(size) +except httplib.IncompleteRead as e: +# e.expected is an integer if length known or None otherwise. +if e.expected: +got = len(e.partial) +total = e.expected + got +msg = _('HTTP request error (incomplete response; ' +'expected %d bytes got %d)') % (total, got) +else: +msg = _('HTTP request error (incomplete response)') + +raise error.PeerTransportError( +msg, +hint=_('this may be an intermittent network failure; ' + 'if the error persists, consider contacting the ' + 'network or server operator')) +except httplib.HTTPException as e: +raise error.PeerTransportError( +_('HTTP request error (%s)') % e, +hint=_('this may be an intermittent network failure; ' + 'if the error persists, consider contacting the ' + 'network or server operator')) + +resp.__class__ = readerproxy diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -69,42 +69,6 @@ return result -def _wraphttpresponse(resp): -"""Wrap an HTTPResponse with common error handlers. - -This ensures that any I/O from any consumer raises the appropriate -error and messaging. -""" -origread = resp.read - -class readerproxy(resp.__class__): -def read(self, size=None): -try: -return origread(size) -except httplib.IncompleteRead as e: -# e.expected is an integer if length known or None otherwise. -if e.expected: -got = len(e.partial) -total = e.expected + got -msg = _('HTTP request error (incomplete response; ' -'expected %d bytes got %d)') % (total, got) -else: -msg = _('HTTP request error (incomplete response)') - -raise error.PeerTransportError( -msg, -hint=_('this may be an intermittent network failure; ' - 'if the error persists, consider contacting the ' - 'network or server operator')) -except httplib.HTTPException as e: -raise error.PeerTransportError( -_('HTTP request error (%s)') % e, -hint=_('this may be an intermittent network failure; ' - 'if the error persists, consider contacting the ' - 'network or server operator')) - -resp.__class__ = readerproxy - class _multifile(object): def __init__(self, *fileobjs): for f in fileobjs: @@ -325,7 +289,7 @@ % (util.timer() - start, code)) # Insert error handlers for common I/O failures. -_wraphttpresponse(res) +urlmod.wrapresponse(res) return res To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4803: storageutil: extract most of emitrevisions() to standalone function
This revision was automatically updated to reflect the committed changes. Closed by commit rHG842ffcf1d42f: storageutil: extract most of emitrevisions() to standalone function (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4803?vs=11486=11617 REVISION DETAIL https://phab.mercurial-scm.org/D4803 AFFECTED FILES mercurial/revlog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -262,3 +262,149 @@ futurelargelinkrevs.add(plinkrev) return strippoint, brokenrevs + +def emitrevisions(store, revs, resultcls, deltaparentfn, candeltafn, + rawsizefn, revdifffn, flagsfn, sendfulltext=False, + revisiondata=False, assumehaveparentrevisions=False, + deltaprevious=False): +"""Generic implementation of ifiledata.emitrevisions(). + +Emitting revision data is subtly complex. This function attempts to +encapsulate all the logic for doing so in a backend-agnostic way. + +``store`` + Object conforming to ``ifilestorage`` interface. + +``revs`` + List of integer revision numbers whose data to emit. + +``resultcls`` + A type implementing the ``irevisiondelta`` interface that will be + constructed and returned. + +``deltaparentfn`` + Callable receiving a revision number and returning the revision number + of a revision that the internal delta is stored against. This delta + will be preferred over computing a new arbitrary delta. + +``candeltafn`` + Callable receiving a pair of revision numbers that returns a bool + indicating whether a delta between them can be produced. + +``rawsizefn`` + Callable receiving a revision number and returning the length of the + ``store.revision(rev, raw=True)``. + +``revdifffn`` + Callable receiving a pair of revision numbers that returns a delta + between them. + +``flagsfn`` + Callable receiving a revision number and returns the integer flags + value for it. + +``sendfulltext`` + Whether to send fulltext revisions instead of deltas, if allowed. + +``revisiondata`` +``assumehaveparentrevisions`` +``deltaprevious`` + See ``ifiledata.emitrevisions()`` interface documentation. +""" + +fnode = store.node + +prevrev = None + +if deltaprevious or assumehaveparentrevisions: +prevrev = store.parentrevs(revs[0])[0] + +# Set of revs available to delta against. +available = set() + +for rev in revs: +if rev == nullrev: +continue + +node = fnode(rev) +deltaparentrev = deltaparentfn(rev) +p1rev, p2rev = store.parentrevs(rev) + +# Forced delta against previous mode. +if deltaprevious: +baserev = prevrev + +# We're instructed to send fulltext. Honor that. +elif sendfulltext: +baserev = nullrev + +# There is a delta in storage. We try to use that because it +# amounts to effectively copying data from storage and is +# therefore the fastest. +elif deltaparentrev != nullrev: +# Base revision was already emitted in this group. We can +# always safely use the delta. +if deltaparentrev in available: +baserev = deltaparentrev + +# Base revision is a parent that hasn't been emitted already. +# Use it if we can assume the receiver has the parent revision. +elif (assumehaveparentrevisions + and deltaparentrev in (p1rev, p2rev)): +baserev = deltaparentrev + +# No guarantee the receiver has the delta parent. Send delta +# against last revision (if possible), which in the common case +# should be similar enough to this revision that the delta is +# reasonable. +elif prevrev is not None: +baserev = prevrev +else: +baserev = nullrev + +# Storage has a fulltext revision. + +# Let's use the previous revision, which is as good a guess as any. +# There is definitely room to improve this logic. +elif prevrev is not None: +baserev = prevrev +else: +baserev = nullrev + +# But we can't actually use our chosen delta base for whatever +# reason. Reset to fulltext. +if baserev != nullrev and not candeltafn(baserev, rev): +baserev = nullrev + +revision = None +delta = None +baserevisionsize = None + +if revisiondata: +if store.iscensored(baserev) or store.iscensored(rev): +try: +revision =
D4805: storageutil: pass nodes into emitrevisions()
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf5d819d84461: storageutil: pass nodes into emitrevisions() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4805?vs=11488=11619 REVISION DETAIL https://phab.mercurial-scm.org/D4805 AFFECTED FILES mercurial/revlog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -17,6 +17,7 @@ nullrev, ) from .. import ( +dagop, error, mdiff, pycompat, @@ -264,8 +265,8 @@ return strippoint, brokenrevs -def emitrevisions(store, revs, resultcls, deltaparentfn=None, candeltafn=None, - rawsizefn=None, revdifffn=None, flagsfn=None, +def emitrevisions(store, nodes, nodesorder, resultcls, deltaparentfn=None, + candeltafn=None, rawsizefn=None, revdifffn=None, flagsfn=None, sendfulltext=False, revisiondata=False, assumehaveparentrevisions=False, deltaprevious=False): @@ -277,8 +278,8 @@ ``store`` Object conforming to ``ifilestorage`` interface. -``revs`` - List of integer revision numbers whose data to emit. +``nodes`` + List of revision nodes whose data to emit. ``resultcls`` A type implementing the ``irevisiondelta`` interface that will be @@ -322,13 +323,23 @@ ``sendfulltext`` Whether to send fulltext revisions instead of deltas, if allowed. +``nodesorder`` ``revisiondata`` ``assumehaveparentrevisions`` ``deltaprevious`` See ``ifiledata.emitrevisions()`` interface documentation. """ fnode = store.node +frev = store.rev + +if nodesorder == 'nodes': +revs = [frev(n) for n in nodes] +elif nodesorder == 'storage': +revs = sorted(frev(n) for n in nodes) +else: +revs = set(frev(n) for n in nodes) +revs = dagop.linearize(revs, store.parentrevs) prevrev = None diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2192,19 +2192,8 @@ if nodesorder is None and not self._generaldelta: nodesorder = 'storage' -frev = self.rev - -if nodesorder == 'nodes': -revs = [frev(n) for n in nodes] -elif nodesorder == 'storage': -revs = sorted(frev(n) for n in nodes) -else: -assert self._generaldelta -revs = set(frev(n) for n in nodes) -revs = dagop.linearize(revs, self.parentrevs) - return storageutil.emitrevisions( -self, revs, revlogrevisiondelta, +self, nodes, nodesorder, revlogrevisiondelta, deltaparentfn=self.deltaparent, candeltafn=self.candelta, rawsizefn=self.rawsize, To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4804: storageutil: make all callables optional
This revision was automatically updated to reflect the committed changes. Closed by commit rHG631c6f5058b9: storageutil: make all callables optional (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4804?vs=11487=11618 REVISION DETAIL https://phab.mercurial-scm.org/D4804 AFFECTED FILES mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -18,6 +18,7 @@ ) from .. import ( error, +mdiff, pycompat, ) @@ -263,8 +264,9 @@ return strippoint, brokenrevs -def emitrevisions(store, revs, resultcls, deltaparentfn, candeltafn, - rawsizefn, revdifffn, flagsfn, sendfulltext=False, +def emitrevisions(store, revs, resultcls, deltaparentfn=None, candeltafn=None, + rawsizefn=None, revdifffn=None, flagsfn=None, + sendfulltext=False, revisiondata=False, assumehaveparentrevisions=False, deltaprevious=False): """Generic implementation of ifiledata.emitrevisions(). @@ -282,26 +284,40 @@ A type implementing the ``irevisiondelta`` interface that will be constructed and returned. -``deltaparentfn`` +``deltaparentfn`` (optional) Callable receiving a revision number and returning the revision number of a revision that the internal delta is stored against. This delta will be preferred over computing a new arbitrary delta. -``candeltafn`` + If not defined, a delta will always be computed from raw revision + data. + +``candeltafn`` (optional) Callable receiving a pair of revision numbers that returns a bool indicating whether a delta between them can be produced. -``rawsizefn`` + If not defined, it is assumed that any two revisions can delta with + each other. + +``rawsizefn`` (optional) Callable receiving a revision number and returning the length of the ``store.revision(rev, raw=True)``. -``revdifffn`` + If not defined, ``len(store.revision(rev, raw=True))`` will be called. + +``revdifffn`` (optional) Callable receiving a pair of revision numbers that returns a delta between them. -``flagsfn`` + If not defined, a delta will be computed by invoking mdiff code + on ``store.revision()`` results. + + Defining this function allows a precomputed or stored delta to be + used without having to compute on. + +``flagsfn`` (optional) Callable receiving a revision number and returns the integer flags - value for it. + value for it. If not defined, flags value will be 0. ``sendfulltext`` Whether to send fulltext revisions instead of deltas, if allowed. @@ -327,9 +343,13 @@ continue node = fnode(rev) -deltaparentrev = deltaparentfn(rev) p1rev, p2rev = store.parentrevs(rev) +if deltaparentfn: +deltaparentrev = deltaparentfn(rev) +else: +deltaparentrev = nullrev + # Forced delta against previous mode. if deltaprevious: baserev = prevrev @@ -373,7 +393,7 @@ # But we can't actually use our chosen delta base for whatever # reason. Reset to fulltext. -if baserev != nullrev and not candeltafn(baserev, rev): +if baserev != nullrev and (candeltafn and not candeltafn(baserev, rev)): baserev = nullrev revision = None @@ -388,21 +408,30 @@ revision = e.tombstone if baserev != nullrev: -baserevisionsize = rawsizefn(baserev) +if rawsizefn: +baserevisionsize = rawsizefn(baserev) +else: +baserevisionsize = len(store.revision(baserev, + raw=True)) elif baserev == nullrev and not deltaprevious: revision = store.revision(node, raw=True) available.add(rev) else: -delta = revdifffn(baserev, rev) +if revdifffn: +delta = revdifffn(baserev, rev) +else: +delta = mdiff.textdiff(store.revision(baserev, raw=True), + store.revision(rev, raw=True)) + available.add(rev) yield resultcls( node=node, p1node=fnode(p1rev), p2node=fnode(p2rev), basenode=fnode(baserev), -flags=flagsfn(rev), +flags=flagsfn(rev) if flagsfn else 0, baserevisionsize=baserevisionsize, revision=revision, delta=delta) To:
D4799: storageutil: extract functionality for resolving strip revisions
This revision was automatically updated to reflect the committed changes. Closed by commit rHGfa3dc85a747e: storageutil: extract functionality for resolving strip revisions (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4799?vs=11482=11613 REVISION DETAIL https://phab.mercurial-scm.org/D4799 AFFECTED FILES mercurial/revlog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -14,6 +14,7 @@ from ..node import ( bin, nullid, +nullrev, ) from .. import ( error, @@ -155,3 +156,54 @@ pass raise error.LookupError(fileid, identifier, _('no match found')) + +def resolvestripinfo(minlinkrev, tiprev, headrevs, linkrevfn, parentrevsfn): +"""Resolve information needed to strip revisions. + +Finds the minimum revision number that must be stripped in order to +strip ``minlinkrev``. + +Returns a 2-tuple of the minimum revision number to do that and a set +of all revision numbers that have linkrevs that would be broken +by that strip. + +``tiprev`` is the current tip-most revision. It is ``len(store) - 1``. +``headrevs`` is an iterable of head revisions. +``linkrevfn`` is a callable that receives a revision and returns a linked +revision. +``parentrevsfn`` is a callable that receives a revision number and returns +an iterable of its parent revision numbers. +""" +brokenrevs = set() +strippoint = tiprev + 1 + +heads = {} +futurelargelinkrevs = set() +for head in headrevs: +headlinkrev = linkrevfn(head) +heads[head] = headlinkrev +if headlinkrev >= minlinkrev: +futurelargelinkrevs.add(headlinkrev) + +# This algorithm involves walking down the rev graph, starting at the +# heads. Since the revs are topologically sorted according to linkrev, +# once all head linkrevs are below the minlink, we know there are +# no more revs that could have a linkrev greater than minlink. +# So we can stop walking. +while futurelargelinkrevs: +strippoint -= 1 +linkrev = heads.pop(strippoint) + +if linkrev < minlinkrev: +brokenrevs.add(strippoint) +else: +futurelargelinkrevs.remove(linkrev) + +for p in parentrevsfn(strippoint): +if p != nullrev: +plinkrev = linkrevfn(p) +heads[p] = plinkrev +if plinkrev >= minlinkrev: +futurelargelinkrevs.add(plinkrev) + +return strippoint, brokenrevs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2096,39 +2096,9 @@ Returns a tuple containing the minimum rev and a set of all revs that have linkrevs that will be broken by this strip. """ -brokenrevs = set() -strippoint = len(self) - -heads = {} -futurelargelinkrevs = set() -for head in self.headrevs(): -headlinkrev = self.linkrev(head) -heads[head] = headlinkrev -if headlinkrev >= minlink: -futurelargelinkrevs.add(headlinkrev) - -# This algorithm involves walking down the rev graph, starting at the -# heads. Since the revs are topologically sorted according to linkrev, -# once all head linkrevs are below the minlink, we know there are -# no more revs that could have a linkrev greater than minlink. -# So we can stop walking. -while futurelargelinkrevs: -strippoint -= 1 -linkrev = heads.pop(strippoint) - -if linkrev < minlink: -brokenrevs.add(strippoint) -else: -futurelargelinkrevs.remove(linkrev) - -for p in self.parentrevs(strippoint): -if p != nullrev: -plinkrev = self.linkrev(p) -heads[p] = plinkrev -if plinkrev >= minlink: -futurelargelinkrevs.add(plinkrev) - -return strippoint, brokenrevs +return storageutil.resolvestripinfo(minlink, len(self) - 1, +self.headrevs(), +self.linkrev, self.parentrevs) def strip(self, minlink, transaction): """truncate the revlog on the first revision with a linkrev >= minlink To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4802: storageutil: invert logic of file data comparison
This revision was automatically updated to reflect the committed changes. Closed by commit rHG1470183068b8: storageutil: invert logic of file data comparison (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4802?vs=11485=11616 REVISION DETAIL https://phab.mercurial-scm.org/D4802 AFFECTED FILES mercurial/filelog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -108,8 +108,18 @@ return False -def filerevisiondifferent(store, node, filedata): -"""Determines whether file data is equivalent to a stored node.""" +def filedataequivalent(store, node, filedata): +"""Determines whether file data is equivalent to a stored node. + +Returns True if the passed file data would hash to the same value +as a stored revision and False otherwise. + +When a stored revision is censored, filedata must be empty to have +equivalence. + +When a stored revision has copy metadata, it is ignored as part +of the compare. +""" if filedata.startswith(b'\x01\n'): revisiontext = b'\x01\n\x01\n' + filedata @@ -121,18 +131,18 @@ computednode = hashrevisionsha1(revisiontext, p1, p2) if computednode == node: -return False +return True # Censored files compare against the empty file. if store.iscensored(store.rev(node)): -return filedata != b'' +return filedata == b'' # Renaming a file produces a different hash, even if the data # remains unchanged. Check if that's the case. if store.renamed(node): -return store.read(node) != filedata +return store.read(node) == filedata -return True +return False def iterrevs(storelen, start=0, stop=None): """Iterate over revision numbers in a store.""" diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -135,7 +135,7 @@ returns True if text is different than what is stored. """ -return storageutil.filerevisiondifferent(self, node, text) +return not storageutil.filedataequivalent(self, node, text) def verifyintegrity(self, state): return self._revlog.verifyintegrity(state) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4801: storageutil: extract filelog.cmp() to a standalone function
This revision was automatically updated to reflect the committed changes. Closed by commit rHG422beffd71ba: storageutil: extract filelog.cmp() to a standalone function (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4801?vs=11484=11615 REVISION DETAIL https://phab.mercurial-scm.org/D4801 AFFECTED FILES mercurial/filelog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -108,6 +108,32 @@ return False +def filerevisiondifferent(store, node, filedata): +"""Determines whether file data is equivalent to a stored node.""" + +if filedata.startswith(b'\x01\n'): +revisiontext = b'\x01\n\x01\n' + filedata +else: +revisiontext = filedata + +p1, p2 = store.parents(node) + +computednode = hashrevisionsha1(revisiontext, p1, p2) + +if computednode == node: +return False + +# Censored files compare against the empty file. +if store.iscensored(store.rev(node)): +return filedata != b'' + +# Renaming a file produces a different hash, even if the data +# remains unchanged. Check if that's the case. +if store.renamed(node): +return store.read(node) != filedata + +return True + def iterrevs(storelen, start=0, stop=None): """Iterate over revision numbers in a store.""" step = 1 diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -135,26 +135,7 @@ returns True if text is different than what is stored. """ - -t = text -if text.startswith('\1\n'): -t = '\1\n\1\n' + text - -samehashes = not self._revlog.cmp(node, t) -if samehashes: -return False - -# censored files compare against the empty file -if self.iscensored(self.rev(node)): -return text != '' - -# renaming a file produces a different hash, even if the data -# remains unchanged. Check if it's the case (slow): -if self.renamed(node): -t2 = self.read(node) -return t2 != text - -return True +return storageutil.filerevisiondifferent(self, node, text) def verifyintegrity(self, state): return self._revlog.verifyintegrity(state) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4797: storageutil: implement file identifier resolution method (BC)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0e8836be9541: storageutil: implement file identifier resolution method (BC) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4797?vs=11480=11611 REVISION DETAIL https://phab.mercurial-scm.org/D4797 AFFECTED FILES mercurial/filelog.py mercurial/repository.py mercurial/testing/storage.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -10,10 +10,13 @@ import hashlib import re +from ..i18n import _ from ..node import ( +bin, nullid, ) from .. import ( +error, pycompat, ) @@ -99,3 +102,53 @@ stop = storelen return pycompat.xrange(start, stop, step) + +def fileidlookup(store, fileid, identifier): +"""Resolve the file node for a value. + +``store`` is an object implementing the ``ifileindex`` interface. + +``fileid`` can be: + +* A 20 byte binary node. +* An integer revision number +* A 40 byte hex node. +* A bytes that can be parsed as an integer representing a revision number. + +``identifier`` is used to populate ``error.LookupError`` with an identifier +for the store. + +Raises ``error.LookupError`` on failure. +""" +if isinstance(fileid, int): +return store.node(fileid) + +if len(fileid) == 20: +try: +store.rev(fileid) +return fileid +except error.LookupError: +pass + +if len(fileid) == 40: +try: +rawnode = bin(fileid) +store.rev(rawnode) +return rawnode +except TypeError: +pass + +try: +rev = int(fileid) + +if b'%d' % rev != fileid: +raise ValueError + +try: +return store.node(rev) +except (IndexError, TypeError): +pass +except (ValueError, OverflowError): +pass + +raise error.LookupError(fileid, identifier, _('no match found')) diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -85,21 +85,19 @@ self.assertEqual(f.lookup(nullid), nullid) self.assertEqual(f.lookup(nullrev), nullid) self.assertEqual(f.lookup(hex(nullid)), nullid) - -# String converted to integer doesn't work for nullrev. -with self.assertRaises(error.LookupError): -f.lookup(b'%d' % nullrev) +self.assertEqual(f.lookup(b'%d' % nullrev), nullid) with self.assertRaises(error.LookupError): f.lookup(b'badvalue') -self.assertEqual(f.lookup(hex(nullid)[0:12]), nullid) +with self.assertRaises(error.LookupError): +f.lookup(hex(nullid)[0:12]) with self.assertRaises(error.LookupError): f.lookup(b'-2') -# TODO this is wonky. -self.assertEqual(f.lookup(b'0'), nullid) +with self.assertRaises(error.LookupError): +f.lookup(b'0') with self.assertRaises(error.LookupError): f.lookup(b'1') @@ -197,7 +195,9 @@ self.assertEqual(f.lookup(-1), nullid) self.assertEqual(f.lookup(b'0'), node) self.assertEqual(f.lookup(hex(node)), node) -self.assertEqual(f.lookup(hex(node)[0:12]), node) + +with self.assertRaises(error.LookupError): +f.lookup(hex(node)[0:12]) with self.assertRaises(IndexError): f.lookup(-2) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -1099,9 +1099,6 @@ that can be converted to an integer. Raises ``error.LookupError`` if a ndoe could not be resolved. - -TODO this is only used by debug* commands and can probably be deleted -easily. """ def parents(node): diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -49,7 +49,8 @@ return self._revlog.node(rev) def lookup(self, node): -return self._revlog.lookup(node) +return storageutil.fileidlookup(self._revlog, node, +self._revlog.indexfile) def linkrev(self, rev): return self._revlog.linkrev(rev) To: 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
D4798: storageutil: consistently raise LookupError (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHGad8389ecd3f5: storageutil: consistently raise LookupError (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4798?vs=11481=11612 REVISION DETAIL https://phab.mercurial-scm.org/D4798 AFFECTED FILES mercurial/testing/storage.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -121,7 +121,10 @@ Raises ``error.LookupError`` on failure. """ if isinstance(fileid, int): -return store.node(fileid) +try: +return store.node(fileid) +except IndexError: +raise error.LookupError(fileid, identifier, _('no match found')) if len(fileid) == 20: try: diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -199,13 +199,13 @@ with self.assertRaises(error.LookupError): f.lookup(hex(node)[0:12]) -with self.assertRaises(IndexError): +with self.assertRaises(error.LookupError): f.lookup(-2) with self.assertRaises(error.LookupError): f.lookup(b'-2') -with self.assertRaises(IndexError): +with self.assertRaises(error.LookupError): f.lookup(1) with self.assertRaises(error.LookupError): To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4800: storageutil: extract copy metadata retrieval out of filelog
This revision was automatically updated to reflect the committed changes. Closed by commit rHG1d97a332c6d9: storageutil: extract copy metadata retrieval out of filelog (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4800?vs=11483=11614 REVISION DETAIL https://phab.mercurial-scm.org/D4800 AFFECTED FILES mercurial/filelog.py mercurial/utils/storageutil.py CHANGE DETAILS diff --git a/mercurial/utils/storageutil.py b/mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py +++ b/mercurial/utils/storageutil.py @@ -89,6 +89,25 @@ offset = text.index(b'\x01\n', 2) return text[offset + 2:] +def filerevisioncopied(store, node): +"""Resolve file revision copy metadata. + +Returns ``False`` if the file has no copy metadata. Otherwise a +2-tuple of the source filename and node. +""" +if store.parents(node)[0] != nullid: +return False + +meta = parsemeta(store.revision(node))[0] + +# copy and copyrev occur in pairs. In rare cases due to old bugs, +# one can occur without the other. So ensure both are present to flag +# as a copy. +if meta and b'copy' in meta and b'copyrev' in meta: +return meta[b'copy'], bin(meta[b'copyrev']) + +return False + def iterrevs(storelen, start=0, stop=None): """Iterate over revision numbers in a store.""" step = 1 diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -115,15 +115,7 @@ return self.addrevision(text, transaction, link, p1, p2) def renamed(self, node): -if self.parents(node)[0] != revlog.nullid: -return False -t = self.revision(node) -m = storageutil.parsemeta(t)[0] -# copy and copyrev occur in pairs. In rare cases due to bugs, -# one can occur without the other. -if m and "copy" in m and "copyrev" in m: -return (m["copy"], revlog.bin(m["copyrev"])) -return False +return storageutil.filerevisioncopied(self, node) def size(self, rev): """return the size of a given revision""" To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4795: dagop: extract DAG local heads functionality from revlog
This revision was automatically updated to reflect the committed changes. Closed by commit rHG8af835af0a85: dagop: extract DAG local heads functionality from revlog (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4795?vs=11478=11609 REVISION DETAIL https://phab.mercurial-scm.org/D4795 AFFECTED FILES mercurial/dagop.py mercurial/revlog.py CHANGE DETAILS diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1069,25 +1069,16 @@ return [self.node(r) for r in self.headrevs()] if start is None: -start = nullid -if stop is None: -stop = [] -stoprevs = set([self.rev(n) for n in stop]) -startrev = self.rev(start) -reachable = {startrev} -heads = {startrev} - -parentrevs = self.parentrevs -for r in self.revs(start=startrev + 1): -for p in parentrevs(r): -if p in reachable: -if r not in stoprevs: -reachable.add(r) -heads.add(r) -if p in heads and p not in stoprevs: -heads.remove(p) - -return [self.node(r) for r in heads] +start = nullrev +else: +start = self.rev(start) + +stoprevs = set(self.rev(n) for n in stop or []) + +revs = dagop.headrevssubset(self.revs, self.parentrevs, startrev=start, +stoprevs=stoprevs) + +return [self.node(rev) for rev in revs] def children(self, node): """find the children of a given node""" diff --git a/mercurial/dagop.py b/mercurial/dagop.py --- a/mercurial/dagop.py +++ b/mercurial/dagop.py @@ -773,6 +773,42 @@ return headrevs +def headrevssubset(revsfn, parentrevsfn, startrev=None, stoprevs=None): +"""Returns the set of all revs that have no children with control. + +``revsfn`` is a callable that with no arguments returns an iterator over +all revision numbers in topological order. With a ``start`` argument, it +returns revision numbers starting at that number. + +``parentrevsfn`` is a callable receiving a revision number and returns an +iterable of parent revision numbers, where values can include nullrev. + +``startrev`` is a revision number at which to start the search. + +``stoprevs`` is an iterable of revision numbers that, when encountered, +will stop DAG traversal beyond them. Parents of revisions in this +collection will be heads. +""" +if startrev is None: +startrev = nullrev + +stoprevs = set(stoprevs or []) + +reachable = {startrev} +heads = {startrev} + +for rev in revsfn(start=startrev + 1): +for prev in parentrevsfn(rev): +if prev in reachable: +if rev not in stoprevs: +reachable.add(rev) +heads.add(rev) + +if prev in heads and prev not in stoprevs: +heads.remove(prev) + +return heads + def linearize(revs, parentsfn): """Linearize and topologically sort a list of revisions. To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4794: dagop: extract descendants() from revlog module
This revision was automatically updated to reflect the committed changes. Closed by commit rHG0b24fcd88066: dagop: extract descendants() from revlog module (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4794?vs=11477=11608 REVISION DETAIL https://phab.mercurial-scm.org/D4794 AFFECTED FILES mercurial/dagop.py mercurial/revlog.py CHANGE DETAILS diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -748,25 +748,7 @@ inclusive=inclusive) def descendants(self, revs): -"""Generate the descendants of 'revs' in revision order. - -Yield a sequence of revision numbers starting with a child of -some rev in revs, i.e., each revision is *not* considered a -descendant of itself. Results are ordered by revision number (a -topological sort).""" -first = min(revs) -if first == nullrev: -for i in self: -yield i -return - -seen = set(revs) -for i in self.revs(start=first + 1): -for x in self.parentrevs(i): -if x != nullrev and x in seen: -seen.add(i) -yield i -break +return dagop.descendantrevs(revs, self.revs, self.parentrevs) def findcommonmissing(self, common=None, heads=None): """Return a tuple of the ancestors of common and the ancestors of heads diff --git a/mercurial/dagop.py b/mercurial/dagop.py --- a/mercurial/dagop.py +++ b/mercurial/dagop.py @@ -9,6 +9,9 @@ import heapq +from .node import ( +nullrev, +) from .thirdparty import ( attr, ) @@ -225,6 +228,37 @@ startdepth, stopdepth) return generatorset(gen, iterasc=True) +def descendantrevs(revs, revsfn, parentrevsfn): +"""Generate revision number descendants in revision order. + +Yields revision numbers starting with a child of some rev in +``revs``. Results are ordered by revision number and are +therefore topological. Each revision is not considered a descendant +of itself. + +``revsfn`` is a callable that with no argument iterates over all +revision numbers and with a ``start`` argument iterates over revision +numbers beginning with that value. + +``parentrevsfn`` is a callable that receives a revision number and +returns an iterable of parent revision numbers, whose values may include +nullrev. +""" +first = min(revs) + +if first == nullrev: +for rev in revsfn(): +yield rev +return + +seen = set(revs) +for rev in revsfn(start=first + 1): +for prev in parentrevsfn(rev): +if prev != nullrev and prev in seen: +seen.add(rev) +yield rev +break + def _reachablerootspure(repo, minroot, roots, heads, includepath): """return (heads(:: and ::)) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4796: testing: add more testing for ifileindex.lookup()
This revision was automatically updated to reflect the committed changes. Closed by commit rHG215fd73cfe52: testing: add more testing for ifileindex.lookup() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4796?vs=11479=11610 REVISION DETAIL https://phab.mercurial-scm.org/D4796 AFFECTED FILES mercurial/testing/storage.py CHANGE DETAILS diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -90,6 +90,30 @@ with self.assertRaises(error.LookupError): f.lookup(b'%d' % nullrev) +with self.assertRaises(error.LookupError): +f.lookup(b'badvalue') + +self.assertEqual(f.lookup(hex(nullid)[0:12]), nullid) + +with self.assertRaises(error.LookupError): +f.lookup(b'-2') + +# TODO this is wonky. +self.assertEqual(f.lookup(b'0'), nullid) + +with self.assertRaises(error.LookupError): +f.lookup(b'1') + +with self.assertRaises(error.LookupError): +f.lookup(b'11') + +for i in range(-5, 5): +if i == nullrev: +continue + +with self.assertRaises(LookupError): +f.lookup(i) + self.assertEqual(f.linkrev(nullrev), nullrev) for i in range(-5, 5): @@ -170,8 +194,22 @@ self.assertEqual(f.lookup(node), node) self.assertEqual(f.lookup(0), node) +self.assertEqual(f.lookup(-1), nullid) self.assertEqual(f.lookup(b'0'), node) self.assertEqual(f.lookup(hex(node)), node) +self.assertEqual(f.lookup(hex(node)[0:12]), node) + +with self.assertRaises(IndexError): +f.lookup(-2) + +with self.assertRaises(error.LookupError): +f.lookup(b'-2') + +with self.assertRaises(IndexError): +f.lookup(1) + +with self.assertRaises(error.LookupError): +f.lookup(b'1') self.assertEqual(f.linkrev(0), 0) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4791: localrepo: define storage backend in creation options (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHGdbcb466d0065: localrepo: define storage backend in creation options (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4791?vs=11474=11605 REVISION DETAIL https://phab.mercurial-scm.org/D4791 AFFECTED FILES mercurial/configitems.py mercurial/localrepo.py mercurial/upgrade.py CHANGE DETAILS diff --git a/mercurial/upgrade.py b/mercurial/upgrade.py --- a/mercurial/upgrade.py +++ b/mercurial/upgrade.py @@ -199,7 +199,8 @@ @staticmethod def _newreporequirements(ui): -return localrepo.newreporequirements(ui) +return localrepo.newreporequirements( +ui, localrepo.defaultcreateopts(ui)) @classmethod def fromrepo(cls, repo): @@ -747,7 +748,8 @@ # FUTURE there is potentially a need to control the wanted requirements via # command arguments or via an extension hook point. -newreqs = localrepo.newreporequirements(repo.ui) +newreqs = localrepo.newreporequirements( +repo.ui, localrepo.defaultcreateopts(repo.ui)) newreqs.update(preservedrequirements(repo)) noremovereqs = (repo.requirements - newreqs - diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -2808,14 +2808,26 @@ def islocal(path): return True -def newreporequirements(ui, createopts=None): +def defaultcreateopts(ui, createopts=None): +"""Populate the default creation options for a repository. + +A dictionary of explicitly requested creation options can be passed +in. Missing keys will be populated. +""" +createopts = dict(createopts or {}) + +if 'backend' not in createopts: +# experimental config: storage.new-repo-backend +createopts['backend'] = ui.config('storage', 'new-repo-backend') + +return createopts + +def newreporequirements(ui, createopts): """Determine the set of requirements for a new local repository. Extensions can wrap this function to specify custom requirements for new repositories. """ -createopts = createopts or {} - # If the repo is being created from a shared repository, we copy # its requirements. if 'sharedrepo' in createopts: @@ -2827,6 +2839,14 @@ return requirements +if 'backend' not in createopts: +raise error.ProgrammingError('backend key not present in createopts; ' + 'was defaultcreateopts() called?') + +if createopts['backend'] != 'revlogv1': +raise error.Abort(_('unable to determine repository requirements for ' +'storage backend: %s') % createopts['backend']) + requirements = {'revlogv1'} if ui.configbool('format', 'usestore'): requirements.add('store') @@ -2885,6 +2905,7 @@ they know how to handle. """ known = { +'backend', 'narrowfiles', 'sharedrepo', 'sharedrelative', @@ -2901,6 +2922,8 @@ The following keys for ``createopts`` are recognized: +backend + The storage backend to use. narrowfiles Set up repository to support narrow file storage. sharedrepo @@ -2912,7 +2935,7 @@ shareditems Set of items to share to the new repository (in addition to storage). """ -createopts = createopts or {} +createopts = defaultcreateopts(ui, createopts=createopts) unknownopts = filterknowncreateopts(ui, createopts) diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -941,6 +941,9 @@ coreconfigitem('push', 'pushvars.server', default=False, ) +coreconfigitem('storage', 'new-repo-backend', +default='revlogv1', +) coreconfigitem('storage', 'revlog.optimize-delta-parent-choice', default=True, alias=[('format', 'aggressivemergedeltas')], To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4793: filelog: remove checkhash() (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG44c98cbc665f: filelog: remove checkhash() (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4793?vs=11476=11607 REVISION DETAIL https://phab.mercurial-scm.org/D4793 AFFECTED FILES mercurial/filelog.py mercurial/repository.py mercurial/testing/storage.py CHANGE DETAILS diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -368,12 +368,6 @@ with self.assertRaises(IndexError): f.size(i) -with self.assertRaises(error.StorageError): -f.checkhash(b'', nullid) - -with self.assertRaises(error.LookupError): -f.checkhash(b'', b'\x01' * 20) - self.assertEqual(f.revision(nullid), b'') self.assertEqual(f.revision(nullid, raw=True), b'') @@ -426,18 +420,6 @@ with self.assertRaises(IndexError): f.size(1) -f.checkhash(fulltext, node) -f.checkhash(fulltext, node, nullid, nullid) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext + b'extra', node) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext, node, b'\x01' * 20, nullid) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext, node, nullid, b'\x01' * 20) - self.assertEqual(f.revision(node), fulltext) self.assertEqual(f.revision(node, raw=True), fulltext) @@ -510,20 +492,6 @@ with self.assertRaises(IndexError): f.size(3) -f.checkhash(fulltext0, node0) -f.checkhash(fulltext1, node1) -f.checkhash(fulltext1, node1, node0, nullid) -f.checkhash(fulltext2, node2, node1, nullid) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext1, b'\x01' * 20) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext1 + b'extra', node1, node0, nullid) - -with self.assertRaises(error.StorageError): -f.checkhash(fulltext1, node1, node0, node0) - self.assertEqual(f.revision(node0), fulltext0) self.assertEqual(f.revision(node0, raw=True), fulltext0) self.assertEqual(f.revision(node1), fulltext1) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -545,12 +545,6 @@ Any metadata is excluded from size measurements. """ -def checkhash(fulltext, node, p1=None, p2=None, rev=None): -"""Validate the stored hash of a given fulltext and node. - -Raises ``error.StorageError`` is hash validation fails. -""" - def revision(node, raw=False): Obtain fulltext data for a node. diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -71,10 +71,6 @@ def iscensored(self, rev): return self._revlog.iscensored(rev) -# Might be unused. -def checkhash(self, text, node, p1=None, p2=None, rev=None): -return self._revlog.checkhash(text, node, p1=p1, p2=p2, rev=rev) - def revision(self, node, _df=None, raw=False): return self._revlog.revision(node, _df=_df, raw=raw) To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4792: filelog: remove revdiff() (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG2f80eaf38ed4: filelog: remove revdiff() (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4792?vs=11475=11606 REVISION DETAIL https://phab.mercurial-scm.org/D4792 AFFECTED FILES mercurial/filelog.py mercurial/repository.py mercurial/testing/storage.py tests/simplestorerepo.py CHANGE DETAILS diff --git a/tests/simplestorerepo.py b/tests/simplestorerepo.py --- a/tests/simplestorerepo.py +++ b/tests/simplestorerepo.py @@ -487,16 +487,6 @@ return nodes -def revdiff(self, rev1, rev2): -validaterev(rev1) -validaterev(rev2) - -node1 = self.node(rev1) -node2 = self.node(rev2) - -return mdiff.textdiff(self.revision(node1, raw=True), - self.revision(node2, raw=True)) - def heads(self, start=None, stop=None): # This is copied from revlog.py. if start is None and stop is None: diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -396,17 +396,6 @@ with self.assertRaises(error.LookupError): f.cmp(b'\x01' * 20, b'irrelevant') -self.assertEqual(f.revdiff(nullrev, nullrev), b'') - -with self.assertRaises(IndexError): -f.revdiff(0, nullrev) - -with self.assertRaises(IndexError): -f.revdiff(nullrev, 0) - -with self.assertRaises(IndexError): -f.revdiff(0, 0) - # Emitting empty list is an empty generator. gen = f.emitrevisions([]) with self.assertRaises(StopIteration): @@ -459,14 +448,6 @@ self.assertFalse(f.cmp(node, fulltext)) self.assertTrue(f.cmp(node, fulltext + b'extra')) -self.assertEqual(f.revdiff(0, 0), b'') -self.assertEqual(f.revdiff(nullrev, 0), - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07%s' % - fulltext) - -self.assertEqual(f.revdiff(0, nullrev), - b'\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00') - # Emitting a single revision works. gen = f.emitrevisions([node]) rev = next(gen) @@ -577,14 +558,6 @@ with self.assertRaises(error.LookupError): f.cmp(b'\x01' * 20, b'irrelevant') -self.assertEqual(f.revdiff(0, 1), - b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' + - fulltext1) - -self.assertEqual(f.revdiff(0, 2), - b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x02' + - fulltext2) - # Nodes should be emitted in order. gen = f.emitrevisions([node0, node1, node2], revisiondata=True) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -586,15 +586,6 @@ TODO better document the copy metadata and censoring logic. """ -def revdiff(rev1, rev2): -"""Obtain a delta between two revision numbers. - -Operates on raw data in the store (``revision(node, raw=True)``). - -The returned data is the result of ``bdiff.bdiff`` on the raw -revision data. -""" - def emitrevisions(nodes, nodesorder=None, revisiondata=False, diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -78,9 +78,6 @@ def revision(self, node, _df=None, raw=False): return self._revlog.revision(node, _df=_df, raw=raw) -def revdiff(self, rev1, rev2): -return self._revlog.revdiff(rev1, rev2) - def emitrevisions(self, nodes, nodesorder=None, revisiondata=False, assumehaveparentrevisions=False, deltaprevious=False): To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4790: wireprotov2: derive "required" from presence of default value
This revision was automatically updated to reflect the committed changes. Closed by commit rHG582676acaf6d: wireprotov2: derive required from presence of default value (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D4790?vs=11473=11604 REVISION DETAIL https://phab.mercurial-scm.org/D4790 AFFECTED FILES mercurial/wireprotov2server.py CHANGE DETAILS diff --git a/mercurial/wireprotov2server.py b/mercurial/wireprotov2server.py --- a/mercurial/wireprotov2server.py +++ b/mercurial/wireprotov2server.py @@ -477,9 +477,6 @@ A callable returning the default value for this argument. If not specified, ``None`` will be the default value. - ``required`` - Bool indicating whether the argument is required. - ``example`` An example value for this argument. @@ -535,13 +532,9 @@ raise error.ProgrammingError('%s argument for command %s does not ' 'declare example field' % (arg, name)) -if 'default' in meta and meta.get('required'): -raise error.ProgrammingError('%s argument for command %s is marked ' - 'as required but has a default value' % - (arg, name)) +meta['required'] = 'default' not in meta meta.setdefault('default', lambda: None) -meta.setdefault('required', False) meta.setdefault('validvalues', None) def register(func): @@ -570,14 +563,17 @@ args={ 'noderange': { 'type': 'list', +'default': lambda: None, 'example': [[b'0123456...'], [b'abcdef...']], }, 'nodes': { 'type': 'list', +'default': lambda: None, 'example': [b'0123456...'], }, 'nodesdepth': { 'type': 'int', +'default': lambda: None, 'example': 10, }, 'fields': { @@ -746,7 +742,6 @@ }, 'nodes': { 'type': 'list', -'required': True, 'example': [b'0123456...'], }, 'fields': { @@ -757,7 +752,6 @@ }, 'path': { 'type': 'bytes', -'required': True, 'example': b'foo.txt', } }, @@ -848,7 +842,6 @@ args={ 'namespace': { 'type': 'bytes', -'required': True, 'example': b'ns', }, }, @@ -865,7 +858,6 @@ args={ 'key': { 'type': 'bytes', -'required': True, 'example': b'foo', }, }, @@ -883,7 +875,6 @@ args={ 'nodes': { 'type': 'list', -'required': True, 'example': [b'0123456...'], }, 'haveparents': { @@ -899,7 +890,6 @@ }, 'tree': { 'type': 'bytes', -'required': True, 'example': b'', }, }, @@ -956,22 +946,18 @@ args={ 'namespace': { 'type': 'bytes', -'required': True, 'example': b'ns', }, 'key': { 'type': 'bytes', -'required': True, 'example': b'key', }, 'old': { 'type': 'bytes', -'required': True, 'example': b'old', }, 'new': { 'type': 'bytes', -'required': True, 'example': 'new', }, }, To: indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4803: storageutil: extract most of emitrevisions() to standalone function
durin42 added a comment. Oh, I just got to https://phab.mercurial-scm.org/D4805. I think the helper is still of somewhat limited utility and truly novel storage engines will end up totally reimplementing emitrevisions, but that's fine. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4803 To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4851: streamclone: pass narrowing related info in generatev2() and _walkstreamfiles()
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This patch adds includes and excludes as argument to generatev2() and build a matcher using that and pass that into _walkstreamfiles(). This will help us in filtering files we stream depending on the includes and excludes passed in by the user. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4851 AFFECTED FILES mercurial/streamclone.py CHANGE DETAILS diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py --- a/mercurial/streamclone.py +++ b/mercurial/streamclone.py @@ -16,6 +16,7 @@ branchmap, cacheutil, error, +narrowspec, phases, pycompat, store, @@ -190,8 +191,8 @@ return True # This is it's own function so extensions can override it. -def _walkstreamfiles(repo): -return repo.store.walk() +def _walkstreamfiles(repo, matcher=None): +return repo.store.walk(matcher) def generatev1(repo): """Emit content for version 1 of a streaming clone. @@ -527,7 +528,7 @@ finally: fp.close() -def generatev2(repo): +def generatev2(repo, includes=None, excludes=None): """Emit content for version 2 of a streaming clone. the data stream consists the following entries: @@ -545,8 +546,12 @@ entries = [] totalfilesize = 0 +matcher = None +if includes or excludes: +matcher = narrowspec.match(repo.root, includes, excludes) + repo.ui.debug('scanning\n') -for name, ename, size in _walkstreamfiles(repo): +for name, ename, size in _walkstreamfiles(repo, matcher): if size: entries.append((_srcstore, name, _fileappend, size)) totalfilesize += size To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4850: store: pass matcher to store.datafiles() and filter files according to it
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY To get narrow stream clones working, we need a way to filter the storage files using a matcher. This patch adds matcher as an argument to store.walk() and store.datafiles() so that we can filter the files returned according to the matcher. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4850 AFFECTED FILES mercurial/store.py CHANGE DETAILS diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -374,17 +374,21 @@ l.sort() return l -def datafiles(self): +def datafiles(self, matcher=None): return self._walk('data', True) + self._walk('meta', True) def topfiles(self): # yield manifest before changelog return reversed(self._walk('', False)) -def walk(self): -'''yields (unencoded, encoded, size)''' +def walk(self, matcher=None): +'''yields (unencoded, encoded, size) + +if a matcher is passed, storage files of only those tracked paths +are passed with matches the matcher +''' # yield data files first -for x in self.datafiles(): +for x in self.datafiles(matcher): yield x for x in self.topfiles(): yield x @@ -422,12 +426,14 @@ self.vfs = vfsmod.filtervfs(vfs, encodefilename) self.opener = self.vfs -def datafiles(self): +def datafiles(self, matcher=None): for a, b, size in super(encodedstore, self).datafiles(): try: a = decodefilename(a) except KeyError: a = None +if matcher and not matcher(_gettrackedpath(a)): +continue yield a, b, size def join(self, f): @@ -551,8 +557,10 @@ def getsize(self, path): return self.rawvfs.stat(path).st_size -def datafiles(self): +def datafiles(self, matcher=None): for f in sorted(self.fncache): +if matcher and not matcher(_gettrackedpath(f)): +continue ef = self.encode(f) try: yield f, ef, self.getsize(ef) To: pulkit, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4849: store: introduce a function to get tracked path from a fncache entry
pulkit created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This patch introduces a function to get the trakced path from a fncache entry. This will be used to filter out files to streamclone using a narrowmatcher. The function decodes the entries, and then returns the part after omitting the ending .d, .i and starting meta/ and data/. I am not sure if this is correct. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4849 AFFECTED FILES mercurial/store.py CHANGE DETAILS diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -24,6 +24,21 @@ parsers = policy.importmod(r'parsers') +def _gettrackedpath(path): +"""parses a fncahe entry and returns the path which that +entry is tracking. + +If None is returned, it means it does not track any path. +""" +path = decodefilename(path) +if path.startswith('data/'): +return path[5:-2] +elif path.startswith('dh/'): +return path[3:-2] +elif path.startswith('meta/'): +return path[5:-2] +return None + # This avoids a collision between a file named foo and a dir named # foo.i or foo.d def _encodedir(path): To: pulkit, #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 07 of 13] rebase: use tuple as `replacement` keys
On 30/09/2018 14:34, Yuya Nishihara wrote: > On Thu, 27 Sep 2018 19:08:39 +0200, Boris Feld wrote: >> # HG changeset patch >> # User Boris Feld >> # Date 1537998671 -7200 >> # Wed Sep 26 23:51:11 2018 +0200 >> # Node ID 79a0f8fcb5c1af9a8ad5dd292a8ef67d75b6779d >> # Parent 77b0a61c3cc5be70c3686f9b7217e59fbf98f5c7 >> # EXP-Topic trackfold >> # Available At https://bitbucket.org/octobus/mercurial-devel/ >> # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r >> 79a0f8fcb5c1 >> rebase: use tuple as `replacement` keys >> >> Now that `cleanupnodes` support tuples as key, we update the rebase code to >> use >> them. No changes in the replacement tracked are introduced yet. >> >> diff --git a/hgext/rebase.py b/hgext/rebase.py >> --- a/hgext/rebase.py >> +++ b/hgext/rebase.py >> @@ -1777,15 +1777,16 @@ def clearrebased(ui, repo, destmap, stat >> else: >> succs = (newnode,) >> if succs is not None: >> -replacements[oldnode] = succs >> +replacements[(oldnode,)] = succs > Somewhat related to this. Is there any reason to build replacements as a dict? > I don't think we'll ever want to lookup succs by multi-oldnodes. I don't think so, using a dict is probably an artifact of when this code belonged to rebase or histedit. Here, a dict was probably used to ease transitive update of finals successors. > ___ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4803: storageutil: extract most of emitrevisions() to standalone function
durin42 requested changes to this revision. durin42 added inline comments. This revision now requires changes to proceed. INLINE COMMENTS > storageutil.py:266 > + > +def emitrevisions(store, revs, resultcls, deltaparentfn, candeltafn, > + rawsizefn, revdifffn, flagsfn, sendfulltext=False, This is a huge step backwards for remotefilelog and friends: it's all defined in terms of revs, whereas emitrevisions (as copypasta-inducing as it was) was in terms of nodes. We need to do better. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4803 To: indygreg, #hg-reviewers, durin42 Cc: durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D4713: largefiles: automatically load largefiles extension when required (BC)
yuja added a comment. > The only data point I have is what I did, which was to only enable it on the command line when cloning. The clone command modifying the local hgrc if necessary handled the rest. I only started to do that because I got annoyed at how simply having it loaded could alter certain commands. IDK if command line enabling was ever documented as the preferred alternative, but that's how I described it in https://phab.mercurial-scm.org/rHGe1dbe0b215ae137eec53ceb12440536d570a83d2. > > At this point, I can't recall specific issues with globally enabling, and they might be mostly fixed. They definitely aren't all fixed. That said, this implicit loading on demand seems roughly equivalent to the previous behavior, so I'm not sure that this would be any more surprising. Repos on the command line would be isolated, but not with commandserver because of the global function wrapping. But I'm guessing most people don't understand that about commandserver. IIUC, we would have to enable the largefiles extension once to clone the repo, but that's no longer needed. So you can enable the extension without hearing its name at all. Perhaps, a warning can be displayed when the non-core repository requirement is added? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4713 To: indygreg, #hg-reviewers Cc: mharbison72, yuja, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: D4713: largefiles: automatically load largefiles extension when required (BC)
> The only data point I have is what I did, which was to only enable it on > the command line when cloning. The clone command modifying the local hgrc if > necessary handled the rest. I only started to do that because I got annoyed > at how simply having it loaded could alter certain commands. IDK if command > line enabling was ever documented as the preferred alternative, but that's > how I described it in > https://phab.mercurial-scm.org/rHGe1dbe0b215ae137eec53ceb12440536d570a83d2. > > At this point, I can't recall specific issues with globally enabling, and > they might be mostly fixed. They definitely aren't all fixed. That said, > this implicit loading on demand seems roughly equivalent to the previous > behavior, so I'm not sure that this would be any more surprising. Repos on > the command line would be isolated, but not with commandserver because of the > global function wrapping. But I'm guessing most people don't understand that > about commandserver. IIUC, we would have to enable the largefiles extension once to clone the repo, but that's no longer needed. So you can enable the extension without hearing its name at all. Perhaps, a warning can be displayed when the non-core repository requirement is added? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
mercurial@39966: new changeset
New changeset in mercurial: https://www.mercurial-scm.org/repo/hg/rev/707c3804e607 changeset: 39966:707c3804e607 bookmark:@ tag: tip user:Martin von Zweigbergk date:Fri Sep 28 12:56:57 2018 -0700 summary: narrow: move copies overrides to core -- Repository URL: https://www.mercurial-scm.org/repo/hg ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 6 of 6] help: document about "export" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538574499 -32400 # Wed Oct 03 22:48:19 2018 +0900 # Node ID 4b485ae732aea8285de993cecc59e60a22780c53 # Parent 5657857137840bbb4406c7d5ca2b17b99918eb94 help: document about "export" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -2033,6 +2033,14 @@ def export(ui, repo, *changesets, **opts .. container:: verbose + Template: + + The following keywords are supported in addition to the common template + keywords and functions. See also :hg:`help templates`. + + :diff:String. Diff content. + :parents: List of strings. Parent nodes of the changeset. + Examples: - use export and import to transplant a bugfix to the current ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 5 of 6] help: document about "config" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538574237 -32400 # Wed Oct 03 22:43:57 2018 +0900 # Node ID 5657857137840bbb4406c7d5ca2b17b99918eb94 # Parent 221a266fb0313847e30acb095e080aee3d955a8d help: document about "config" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1729,6 +1729,16 @@ def config(ui, repo, *values, **opts): See :hg:`help config` for more information about config files. +.. container:: verbose + + Template: + + The following keywords are supported. See also :hg:`help templates`. + + :name:String. Config name. + :source: String. Filename and line number where the item is defined. + :value: String. Config value. + Returns 0 on success, 1 if NAME does not exist. """ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 6] help: document about "cat" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538573658 -32400 # Wed Oct 03 22:34:18 2018 +0900 # Node ID 221a266fb0313847e30acb095e080aee3d955a8d # Parent 1d6252375718965479b2602fa7558436a08cbeb1 help: document about "cat" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1358,6 +1358,16 @@ def cat(ui, repo, file1, *pats, **opts): :``%b``: basename of the exporting repository :``\\``: literal "\\" character +.. container:: verbose + + Template: + + The following keywords are supported in addition to the common template + keywords and functions. See also :hg:`help templates`. + + :data:String. File content. + :path:String. Repository-absolute path of the file. + Returns 0 on success. """ opts = pycompat.byteskwargs(opts) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 6] help: document about "branches" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538573929 -32400 # Wed Oct 03 22:38:49 2018 +0900 # Node ID 1d6252375718965479b2602fa7558436a08cbeb1 # Parent 6d44024b584d8e8c35d693eddd87e368aa8ea3dd help: document about "branches" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1132,6 +1132,18 @@ def branches(ui, repo, active=False, clo Use the command :hg:`update` to switch to an existing branch. +.. container:: verbose + + Template: + + The following keywords are supported in addition to the common template + keywords and functions such as ``{branch}``. See also + :hg:`help templates`. + + :active: Boolean. True if the branch is active. + :closed: Boolean. True if the branch is closed. + :current: Boolean. True if it is the current branch. + Returns 0. """ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 6] help: document about "bookmarks" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538573538 -32400 # Wed Oct 03 22:32:18 2018 +0900 # Node ID 6d44024b584d8e8c35d693eddd87e368aa8ea3dd # Parent 79f940d8a9daa1ad391d5bc9cc1c19be37e6a484 help: document about "bookmarks" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -954,6 +954,14 @@ def bookmark(ui, repo, *names, **opts): .. container:: verbose + Template: + + The following keywords are supported in addition to the common template + keywords and functions such as ``{bookmark}``. See also + :hg:`help templates`. + + :active: Boolean. True if the bookmark is active. + Examples: - create an active bookmark for a new line of development:: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 6] help: document about "annotate" template keywords
# HG changeset patch # User Yuya Nishihara # Date 1538573265 -32400 # Wed Oct 03 22:27:45 2018 +0900 # Node ID 79f940d8a9daa1ad391d5bc9cc1c19be37e6a484 # Parent 6962ebc8f817c2df412d1ab4070088909d32fa05 help: document about "annotate" template keywords diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -288,6 +288,25 @@ def annotate(ui, repo, *pats, **opts): anyway, although the results will probably be neither useful nor desirable. +.. container:: verbose + + Template: + + The following keywords are supported in addition to the common template + keywords and functions. See also :hg:`help templates`. + + :lines: List of lines with annotation data. + :path:String. Repository-absolute path of the specified file. + + And each entry of ``{lines}`` provides the following sub-keywords in + addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc. + + :line:String. Line content. + :lineno: Integer. Line number at that revision. + :path:String. Repository-absolute path of the file at that revision. + + See :hg:`help templates.operators` for the list expansion syntax. + Returns 0 on success. """ opts = pycompat.byteskwargs(opts) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel