[Bug 5782] New: rebase complains about divergence when rebasing commit with hidden successor
https://bz.mercurial-scm.org/show_bug.cgi?id=5782 Bug ID: 5782 Summary: rebase complains about divergence when rebasing commit with hidden successor Product: Mercurial Version: default branch Hardware: PC OS: Mac OS Status: UNCONFIRMED Severity: feature Priority: wish Component: rebase Assignee: bugzi...@mercurial-scm.org Reporter: martinv...@google.com CC: mercurial-devel@mercurial-scm.org $ hg --hidden log -G -T '{rev} {desc}' x 4 foo successor | | o 3 bar | | | x 2 foo |/ o 1 | o 0 $ hg rebase -d 0 -r 2 abort: this rebase will cause divergences from: 2 <- actually a hash This error message seems misleading since rebasing 2 would not lead to divergence. -- You are receiving this mail because: You are on the CC list for the bug. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH STABLE] fileset: don't abort when running copied() on a revision with a removed file
# HG changeset patch # User Matt Harbison# Date 1517979217 18000 # Tue Feb 06 23:53:37 2018 -0500 # Branch stable # Node ID 7b2b82f891bf6355ed87c06ed9198bfcd033fe7d # Parent 1d60ad093792706e1dc7a52b20942593f2c19655 fileset: don't abort when running copied() on a revision with a removed file It looks like AND with any status-y fileset would trigger this, as added() and removed() also failed. The 4.5-rc revision is a convenient test case, but the merge isn't necessary. diff --git a/mercurial/fileset.py b/mercurial/fileset.py --- a/mercurial/fileset.py +++ b/mercurial/fileset.py @@ -464,9 +464,10 @@ getargs(x, 0, 0, _("copied takes no arguments")) s = [] for f in mctx.subset: -p = mctx.ctx[f].parents() -if p and p[0].path() != f: -s.append(f) +if f in mctx.ctx: +p = mctx.ctx[f].parents() +if p and p[0].path() != f: +s.append(f) return s @predicate('revs(revs, pattern)') diff --git a/tests/test-fileset.t b/tests/test-fileset.t --- a/tests/test-fileset.t +++ b/tests/test-fileset.t @@ -249,6 +249,8 @@ Test merge states $ hg ci -m manychanges + $ hg file -r . 'set:copied() & modified()' + [1] $ hg up -C 0 * files updated, 0 files merged, * files removed, 0 files unresolved (glob) $ echo c >> b2 ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2066: lfs: add a test showing bundle application could be broken
quark updated this revision to Diff 5275. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2066?vs=5272=5275 REVISION DETAIL https://phab.mercurial-scm.org/D2066 AFFECTED FILES tests/drawdag.py tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t new file mode 100644 --- /dev/null +++ b/tests/test-lfs-bundle.t @@ -0,0 +1,100 @@ +In this test, we want to test LFS bundle application on both LFS and non-LFS +repos. + +To make it more interesting, the file revisions will contain hg filelog +metadata ('\1\n'). The bundle will have 1 file revision overlapping with the +destination repo. + +# rev 1 2 3 +# repo:yesyes no +# bundle: no (base) yes yes (deltabase: 2 if possible) + +It is interesting because rev 2 could have been stored as LFS in the repo, and +non-LFS in the bundle; or vice-versa. + +Init: + + $ cat >> $HGRCPATH << EOF + > [extensions] + > lfs= + > drawdag=$TESTDIR/drawdag.py + > [lfs] + > url=file://$TESTTMP/lfs-remote + > EOF + +Helper functions to create commits: + + $ commitxy() { + > hg debugdrawdag "$@" <<'EOS' + > Y # Y/X=\1\n\nE\nF + > | # Y/Y=\1\n\nG\nH + > X # X/X=\1\n\nC\n + > # X/Y=\1\n\nD\n + > EOS + > } + + $ commitz() { + > hg debugdrawdag "$@" <<'EOS' + > Z # Z/X=\1\n\nI\n + > | # Z/Y=\1\n\nJ\n + > | # Z/Z=\1\nZ + > Y + > EOS + > } + + $ enablelfs() { + > cat >> .hg/hgrc < [lfs] + > track=all() + > EOF + > } + +Generate bundles + + $ for i in normal lfs; do + > NAME=src-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > commitz + > hg bundle -q --base X -r Y+Z $TESTTMP/$NAME.bundle + > SRCNAMES="$SRCNAMES $NAME" + > done + +Prepare destination repos + + $ for i in normal lfs; do + > NAME=dst-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > DSTNAMES="$DSTNAMES $NAME" + > done + +Apply bundles + + $ for i in $SRCNAMES; do + > for j in $DSTNAMES; do + > echo Applying $i.bundle to $j + > cp -R $TESTTMP/$j $TESTTMP/tmp-$i-$j + > cd $TESTTMP/tmp-$i-$j + > if hg unbundle $TESTTMP/$i.bundle -q 2>/dev/null; then + > hg verify -q && echo OK + > else + > echo CRASHED + > fi + > done + > done + Applying src-normal.bundle to dst-normal + OK + Applying src-normal.bundle to dst-lfs + X@2: unpacking bcc7d23fa6b7: integrity check failed on data/X.i:2 + Y@2: unpacking 46017a6640e7: integrity check failed on data/Y.i:2 + 2 integrity errors encountered! + (first damaged changeset appears to be 2) + Applying src-lfs.bundle to dst-normal + CRASHED + Applying src-lfs.bundle to dst-lfs + OK diff --git a/tests/drawdag.py b/tests/drawdag.py --- a/tests/drawdag.py +++ b/tests/drawdag.py @@ -371,7 +371,8 @@ comments = list(_getcomments(text)) filere = re.compile(br'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M) for name, path, content in filere.findall(b'\n'.join(comments)): -files[name][path] = content.replace(br'\n', b'\n') +content = content.replace(br'\n', b'\n').replace(br'\1', b'\1') +files[name][path] = content committed = {None: node.nullid} # {name: node} To: quark, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2067: changegroup: do not delta lfs revisions
quark updated this revision to Diff 5273. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2067?vs=5269=5273 REVISION DETAIL https://phab.mercurial-scm.org/D2067 AFFECTED FILES mercurial/changegroup.py mercurial/revlog.py tests/test-lfs-bundle.t tests/test-lfs.t CHANGE DETAILS diff --git a/tests/test-lfs.t b/tests/test-lfs.t --- a/tests/test-lfs.t +++ b/tests/test-lfs.t @@ -349,7 +349,7 @@ uncompressed size of bundle content: * (changelog) (glob) * (manifests) (glob) - * a (glob) + * a (glob) $ hg --config extensions.strip= strip -r 2 --no-backup --force -q $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a 5 branching diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -95,6 +95,6 @@ 2 integrity errors encountered! (first damaged changeset appears to be 2) Applying src-lfs.bundle to dst-normal - CRASHED + OK Applying src-lfs.bundle to dst-lfs OK diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -713,6 +713,13 @@ except KeyError: return False +def candelta(self, baserev, rev): +"""whether two revisions (prev, rev) can be delta-ed or not""" +# disable delta if either rev uses non-default flag (ex. LFS) +if self.flags(baserev) or self.flags(rev): +return False +return True + def clearcaches(self): self._cache = None self._chainbasecache.clear() diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -770,6 +770,8 @@ progress(msgbundling, None) def deltaparent(self, revlog, rev, p1, p2, prev): +if not revlog.candelta(prev, rev): +raise error.ProgrammingError('cg1 should not be used in this case') return prev def revchunk(self, revlog, rev, prev, linknode): @@ -829,16 +831,19 @@ # expensive. The revlog caches should have prev cached, meaning # less CPU for changegroup generation. There is likely room to add # a flag and/or config option to control this behavior. -return prev +base = prev elif dp == nullrev: # revlog is configured to use full snapshot for a reason, # stick to full snapshot. -return nullrev +base = nullrev elif dp not in (p1, p2, prev): # Pick prev when we can't be sure remote has the base revision. return prev else: -return dp +base = dp +if base != nullrev and not revlog.candelta(base, rev): +base = nullrev +return base def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): # Do nothing with flags, it is implicitly 0 in cg1 and cg2 To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2068: revlog: do not use delta for lfs revisions
quark updated this revision to Diff 5274. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2068?vs=5270=5274 REVISION DETAIL https://phab.mercurial-scm.org/D2068 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t tests/test-revlog-raw.py CHANGE DETAILS diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py --- a/tests/test-revlog-raw.py +++ b/tests/test-revlog-raw.py @@ -114,6 +114,8 @@ else: # suboptimal deltaparent deltaparent = min(0, parentrev) +if not rlog.candelta(deltaparent, r): +deltaparent = -1 return {'node': rlog.node(r), 'p1': pnode, 'p2': node.nullid, 'cs': rlog.node(rlog.linkrev(r)), 'flags': rlog.flags(r), 'deltabase': rlog.node(deltaparent), @@ -151,12 +153,12 @@ for r in rlog: p1 = rlog.node(r - 1) p2 = node.nullid -if r == 0: +if r == 0 or rlog.flags(r): text = rlog.revision(r, raw=True) cachedelta = None else: -# deltaparent is more interesting if it has the EXTSTORED flag. -deltaparent = max([0] + [p for p in range(r - 2) if rlog.flags(p)]) +# deltaparent cannot have EXTSTORED flag. +deltaparent = max([-1] + [p for p in range(r) if not rlog.flags(p)]) text = None cachedelta = (deltaparent, rlog.revdiff(deltaparent, r)) flags = rlog.flags(r) @@ -262,8 +264,9 @@ result.append((text, rawtext)) # Verify flags like isdelta, isext work as expected -if bool(rlog.deltaparent(rev) > -1) != isdelta: -abort('rev %d: isdelta is ineffective' % rev) +# isdelta can be overridden to False if this or p1 has isext set +if bool(rlog.deltaparent(rev) > -1) and not isdelta: +abort('rev %d: isdelta is unexpected' % rev) if bool(rlog.flags(rev)) != isext: abort('rev %d: isext is ineffective' % rev) return result diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -90,10 +90,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - X@2: unpacking bcc7d23fa6b7: integrity check failed on data/X.i:2 - Y@2: unpacking 46017a6640e7: integrity check failed on data/Y.i:2 - 2 integrity errors encountered! - (first damaged changeset appears to be 2) + CRASHED Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -404,6 +404,9 @@ for candidaterevs in self._getcandidaterevs(p1, p2, cachedelta): nominateddeltas = [] for candidaterev in candidaterevs: +# do not use flags != 0 (ex. LFS) revision as delta base +if revlog.flags(candidaterev) != REVIDX_DEFAULT_FLAGS: +continue candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) if revlog._isgooddeltainfo(candidatedelta, revinfo.textlen): nominateddeltas.append(candidatedelta) @@ -2082,7 +2085,12 @@ deltacomputer = _deltacomputer(self) revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags) -deltainfo = deltacomputer.finddeltainfo(revinfo, fh) + +# do not use delta for flags != 0 (ex. LFS) revisions +if flags == REVIDX_DEFAULT_FLAGS: +deltainfo = deltacomputer.finddeltainfo(revinfo, fh) +else: +deltainfo = None if deltainfo is not None: base = deltainfo.base To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2066: lfs: add a test showing bundle application could be broken
quark updated this revision to Diff 5272. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2066?vs=5268=5272 REVISION DETAIL https://phab.mercurial-scm.org/D2066 AFFECTED FILES tests/drawdag.py tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t new file mode 100644 --- /dev/null +++ b/tests/test-lfs-bundle.t @@ -0,0 +1,100 @@ +In this test, we want to test LFS bundle application on both LFS and non-LFS +repos. + +To make it more interesting, the file revisions will contain hg filelog +metadata ('\1\n'). The bundle will have 1 file revision overlapping with the +destination repo. + +# rev 1 2 3 +# repo:yesyes no +# bundle: no (base) yes yes (deltabase: 2 if possible) + +It is interesting because rev 2 could have been stored as LFS in the repo, and +non-LFS in the bundle; or vice-versa. + +Init: + + $ cat >> $HGRCPATH << EOF + > [extensions] + > lfs= + > drawdag=$TESTDIR/drawdag.py + > [lfs] + > url=file://$TESTTMP/lfs-remote + > EOF + +Helper functions to create commits: + + $ commitxy() { + > hg debugdrawdag "$@" <<'EOS' + > Y # Y/X=\1\n\nE\nF + > | # Y/Y=\1\n\nG\nH + > X # X/X=\1\n\nC\n + > # X/Y=\1\n\nD\n + > EOS + > } + + $ commitz() { + > hg debugdrawdag "$@" <<'EOS' + > Z # Z/X=\1\n\nI\n + > | # Z/Y=\1\n\nJ\n + > | # Z/Z=\1\nZ + > Y + > EOS + > } + + $ enablelfs() { + > cat >> .hg/hgrc < [lfs] + > threshold=1 + > EOF + > } + +Generate bundles + + $ for i in normal lfs; do + > NAME=src-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > commitz + > hg bundle -q --base X -r Y+Z $TESTTMP/$NAME.bundle + > SRCNAMES="$SRCNAMES $NAME" + > done + +Prepare destination repos + + $ for i in normal lfs; do + > NAME=dst-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > DSTNAMES="$DSTNAMES $NAME" + > done + +Apply bundles + + $ for i in $SRCNAMES; do + > for j in $DSTNAMES; do + > echo Applying $i.bundle to $j + > cp -R $TESTTMP/$j $TESTTMP/tmp-$i-$j + > cd $TESTTMP/tmp-$i-$j + > if hg unbundle $TESTTMP/$i.bundle -q 2>/dev/null; then + > hg verify -q && echo OK + > else + > echo CRASHED + > fi + > done + > done + Applying src-normal.bundle to dst-normal + OK + Applying src-normal.bundle to dst-lfs + X@2: unpacking bcc7d23fa6b7: integrity check failed on data/X.i:2 + Y@2: unpacking 46017a6640e7: integrity check failed on data/Y.i:2 + 2 integrity errors encountered! + (first damaged changeset appears to be 2) + Applying src-lfs.bundle to dst-normal + CRASHED + Applying src-lfs.bundle to dst-lfs + OK diff --git a/tests/drawdag.py b/tests/drawdag.py --- a/tests/drawdag.py +++ b/tests/drawdag.py @@ -371,7 +371,8 @@ comments = list(_getcomments(text)) filere = re.compile(br'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M) for name, path, content in filere.findall(b'\n'.join(comments)): -files[name][path] = content.replace(br'\n', b'\n') +content = content.replace(br'\n', b'\n').replace(br'\1', b'\1') +files[name][path] = content committed = {None: node.nullid} # {name: node} To: quark, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2057: translate base85.c into rust code
Ivzhh added a comment. Thank you @indygreg for your detailed explanation! I understand the process now, and I will go back reading the developer's guide thoroughly again. I will try my best to provide a relatively clean stack of patches. Thank you for you time! REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2061: sshpeer: initial definition and implementation of new SSH protocol
indygreg added a comment. I want to emphasize that I'm not committed to any implementation detail at this point in time. I've very opened minded about alternatives and making backwards incompatible changes throughout the 4.6 release cycle. That being said, I am trying to make forward progress on a ton of wire protocol changes. These are blocking my planned work for shallow clone this release cycle. (I don't want to deploy shallow clone on the existing wire protocol for various reasons.) So, I would prefer we //fall forward// and take commits even if there are open bikesheds. I'm more than happy to rework the protocol later. I just don't want my local work to be dozens of changesets ahead of what's reviewed and have to spend hours reworking my code because of a bikeshed. I'd rather commit the flawed work, fix it at the head of my local queue, and move forward. If nothing else, this approach will lead to a more feature complete protocol landing sooner. And only once it is feature complete will we all have the full perspective to bikeshed the protocol. INLINE COMMENTS > wireprotocol.txt:241-243 > +The transport capabilities string is a URL/percent encoded string > +containing key-value pairs defining the client's transport-level > +capabilities. The following capabilities are defined: I chose the //query string// format here because I don't like reinventing wheels. However, if we wanted to make it a list of space delimited atoms (like the existing capabilities string), I'd be OK with that. We can always change this later, since we're not locked into any BC guarantees at this juncture. > wireprotocol.txt:245-247 > +proto > + A comma-delimited list of transport protocol versions the client > + supports. e.g. ``ssh-v2``. In the future, I want to advertise: - compression engine support - compression engine settings (e.g. max window size for zstandard so a server won't choose a compression level that will result in excessive memory usage for client) - max concurrent responses limit (in the future, the protocol will gain the ability to stream multiple responses for a single request concurrently) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2061 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
D2057: translate base85.c into rust code
indygreg added a comment. We generally prefer that patches to Mercurial be small and do a single thing. This makes it easier to review and understand changes, since each change can be evaluated in isolation. If you submit changesets together using `hg phabsend`, they automatically show up as a //stack// in Phabricator. And if changesets at the bottom of the stack are ready to land, we generally land those without waiting for the entire stack to land. This enables forward progress to be made and this is generally better for everyone than waiting until a series of commits is perfect before adding any of them. What that means is you should ideally split this work into smaller parts. For example: 1. Add the pure Rust code/crate 2. Add the Python Rust code/crate 3. Build system / module policy changes I'm not sure of the order of things though. Since this is the first Rust extension, it's not clear what needs to be implemented in what order. I'm fine looking at a large commit if things are too tightly coupled to separate. But you should strive to make smaller commits. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2057: translate base85.c into rust code
Ivzhh added a comment. Sure, thank you for the comments! I can definitely prepare makefile and setup.py to make the building process work with rust part. I am planning to change the policy.py module to support and try to load rust modules and run all the tests. I will submit a new patch after finishing these two tasks. After reading wiki/OxidationPlan again, I plan to change to cffi for better compatibility (pypy and others), and try to build algorithms in pure rust. Shall I wait till migrating to cffi based solution now and resubmit this patch with all three changes (building, testing, and cffi)? Thank you! REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2069: revlog: resolve lfs rawtext to vanilla rawtext before applying delta
quark updated this revision to Diff 5271. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2069?vs=5267=5271 REVISION DETAIL https://phab.mercurial-scm.org/D2069 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -90,7 +90,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - CRASHED + OK Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -333,7 +333,8 @@ len(delta) - hlen): btext[0] = delta[hlen:] else: -basetext = revlog.revision(baserev, _df=fh, raw=True) +# deltabase is flags=0 rawtext, equivalent to non-raw text +basetext = revlog.revision(baserev, _df=fh, raw=False) btext[0] = mdiff.patch(basetext, delta) try: @@ -2076,7 +2077,9 @@ # full versions are inserted when the needed deltas # become comparable to the uncompressed text if rawtext is None: -textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]), +# need flags=0 rawtext size, which is the non-raw size +# use revlog explicitly to avoid filelog's metadata handling +textlen = mdiff.patchedsize(revlog.size(self, cachedelta[0]), cachedelta[1]) else: textlen = len(rawtext) To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2067: changegroup: do not delta lfs revisions
quark updated this revision to Diff 5269. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2067?vs=5264=5269 REVISION DETAIL https://phab.mercurial-scm.org/D2067 AFFECTED FILES mercurial/changegroup.py mercurial/revlog.py tests/test-lfs-bundle.t tests/test-lfs.t CHANGE DETAILS diff --git a/tests/test-lfs.t b/tests/test-lfs.t --- a/tests/test-lfs.t +++ b/tests/test-lfs.t @@ -349,7 +349,7 @@ uncompressed size of bundle content: * (changelog) (glob) * (manifests) (glob) - * a (glob) + * a (glob) $ hg --config extensions.strip= strip -r 2 --no-backup --force -q $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a 5 branching diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -95,6 +95,6 @@ 2 integrity errors encountered! (first damaged changeset appears to be 2) Applying src-lfs.bundle to dst-normal - CRASHED + OK Applying src-lfs.bundle to dst-lfs OK diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -713,6 +713,13 @@ except KeyError: return False +def candelta(self, baserev, rev): +"""whether two revisions (prev, rev) can be delta-ed or not""" +# disable delta if either rev uses non-default flag (ex. LFS) +if self.flags(baserev) or self.flags(rev): +return False +return True + def clearcaches(self): self._cache = None self._chainbasecache.clear() diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -770,6 +770,8 @@ progress(msgbundling, None) def deltaparent(self, revlog, rev, p1, p2, prev): +if not revlog.candelta(prev, rev): +raise error.ProgrammingError('cg1 should not be used in this case') return prev def revchunk(self, revlog, rev, prev, linknode): @@ -829,16 +831,19 @@ # expensive. The revlog caches should have prev cached, meaning # less CPU for changegroup generation. There is likely room to add # a flag and/or config option to control this behavior. -return prev +base = prev elif dp == nullrev: # revlog is configured to use full snapshot for a reason, # stick to full snapshot. -return nullrev +base = nullrev elif dp not in (p1, p2, prev): # Pick prev when we can't be sure remote has the base revision. return prev else: -return dp +base = dp +if base != nullrev and not revlog.candelta(base, rev): +base = nullrev +return base def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): # Do nothing with flags, it is implicitly 0 in cg1 and cg2 To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2068: revlog: do not use delta for lfs revisions
quark updated this revision to Diff 5270. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2068?vs=5265=5270 REVISION DETAIL https://phab.mercurial-scm.org/D2068 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t tests/test-revlog-raw.py CHANGE DETAILS diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py --- a/tests/test-revlog-raw.py +++ b/tests/test-revlog-raw.py @@ -114,6 +114,8 @@ else: # suboptimal deltaparent deltaparent = min(0, parentrev) +if not rlog.candelta(deltaparent, r): +deltaparent = -1 return {'node': rlog.node(r), 'p1': pnode, 'p2': node.nullid, 'cs': rlog.node(rlog.linkrev(r)), 'flags': rlog.flags(r), 'deltabase': rlog.node(deltaparent), @@ -151,12 +153,12 @@ for r in rlog: p1 = rlog.node(r - 1) p2 = node.nullid -if r == 0: +if r == 0 or rlog.flags(r): text = rlog.revision(r, raw=True) cachedelta = None else: -# deltaparent is more interesting if it has the EXTSTORED flag. -deltaparent = max([0] + [p for p in range(r - 2) if rlog.flags(p)]) +# deltaparent cannot have EXTSTORED flag. +deltaparent = max([-1] + [p for p in range(r) if not rlog.flags(p)]) text = None cachedelta = (deltaparent, rlog.revdiff(deltaparent, r)) flags = rlog.flags(r) @@ -262,8 +264,9 @@ result.append((text, rawtext)) # Verify flags like isdelta, isext work as expected -if bool(rlog.deltaparent(rev) > -1) != isdelta: -abort('rev %d: isdelta is ineffective' % rev) +# isdelta can be overridden to False if this or p1 has isext set +if bool(rlog.deltaparent(rev) > -1) and not isdelta: +abort('rev %d: isdelta is unexpected' % rev) if bool(rlog.flags(rev)) != isext: abort('rev %d: isext is ineffective' % rev) return result diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -90,10 +90,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - X@2: unpacking 0609652b7877: integrity check failed on data/X.i:2 - Y@2: unpacking e384812cdeb9: integrity check failed on data/Y.i:2 - 2 integrity errors encountered! - (first damaged changeset appears to be 2) + CRASHED Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -404,6 +404,9 @@ for candidaterevs in self._getcandidaterevs(p1, p2, cachedelta): nominateddeltas = [] for candidaterev in candidaterevs: +# do not use flags != 0 (ex. LFS) revision as delta base +if revlog.flags(candidaterev) != REVIDX_DEFAULT_FLAGS: +continue candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) if revlog._isgooddeltainfo(candidatedelta, revinfo.textlen): nominateddeltas.append(candidatedelta) @@ -2082,7 +2085,12 @@ deltacomputer = _deltacomputer(self) revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags) -deltainfo = deltacomputer.finddeltainfo(revinfo, fh) + +# do not use delta for flags != 0 (ex. LFS) revisions +if flags == REVIDX_DEFAULT_FLAGS: +deltainfo = deltacomputer.finddeltainfo(revinfo, fh) +else: +deltainfo = None if deltainfo is not None: base = deltainfo.base To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2066: lfs: add a test showing bundle application could be broken
quark updated this revision to Diff 5268. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2066?vs=5263=5268 REVISION DETAIL https://phab.mercurial-scm.org/D2066 AFFECTED FILES tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t new file mode 100644 --- /dev/null +++ b/tests/test-lfs-bundle.t @@ -0,0 +1,100 @@ +In this test, we want to test LFS bundle application on both LFS and non-LFS +repos. + +To make it more interesting, the file revisions will contain hg filelog +metadata ('\1\n'). The bundle will have 1 file revision overlapping with the +destination repo. + +# rev 1 2 3 +# repo:yesyes no +# bundle: no (base) yes yes (deltabase: 2 if possible) + +It is interesting because rev 2 could have been stored as LFS in the repo, and +non-LFS in the bundle; or vice-versa. + +Init: + + $ cat >> $HGRCPATH << EOF + > [extensions] + > lfs= + > drawdag=$TESTDIR/drawdag.py + > [lfs] + > url=file://$TESTTMP/lfs-remote + > EOF + +Helper functions to create commits: + + $ commitxy() { + > hg debugdrawdag "$@" <<'EOS' + > Y # Y/X=\1\n\nE\nF + > | # Y/Y=\1\n\nG\nH + > X # X/X=\1\n\nC\n + > # X/Y=\1\n\nD\n + > EOS + > } + + $ commitz() { + > hg debugdrawdag "$@" <<'EOS' + > Z # Z/X=\1\n\nI\n + > | # Z/Y=\1\n\nJ\n + > | # Z/Z=\1\nZ + > Y + > EOS + > } + + $ enablelfs() { + > cat >> .hg/hgrc < [lfs] + > threshold=1 + > EOF + > } + +Generate bundles + + $ for i in normal lfs; do + > NAME=src-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > commitz + > hg bundle -q --base X -r Y+Z $TESTTMP/$NAME.bundle + > SRCNAMES="$SRCNAMES $NAME" + > done + +Prepare destination repos + + $ for i in normal lfs; do + > NAME=dst-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > DSTNAMES="$DSTNAMES $NAME" + > done + +Apply bundles + + $ for i in $SRCNAMES; do + > for j in $DSTNAMES; do + > echo Applying $i.bundle to $j + > cp -R $TESTTMP/$j $TESTTMP/tmp-$i-$j + > cd $TESTTMP/tmp-$i-$j + > if hg unbundle $TESTTMP/$i.bundle -q 2>/dev/null; then + > hg verify -q && echo OK + > else + > echo CRASHED + > fi + > done + > done + Applying src-normal.bundle to dst-normal + OK + Applying src-normal.bundle to dst-lfs + X@2: unpacking 0609652b7877: integrity check failed on data/X.i:2 + Y@2: unpacking e384812cdeb9: integrity check failed on data/Y.i:2 + 2 integrity errors encountered! + (first damaged changeset appears to be 2) + Applying src-lfs.bundle to dst-normal + CRASHED + Applying src-lfs.bundle to dst-lfs + OK To: quark, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2069: revlog: resolve lfs rawtext to vanilla rawtext before applying delta
quark updated this revision to Diff 5267. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2069?vs=5266=5267 REVISION DETAIL https://phab.mercurial-scm.org/D2069 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -91,7 +91,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - CRASHED + OK Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -333,7 +333,8 @@ len(delta) - hlen): btext[0] = delta[hlen:] else: -basetext = revlog.revision(baserev, _df=fh, raw=True) +# deltabase is flags=0 rawtext, equivalent to non-raw text +basetext = revlog.revision(baserev, _df=fh, raw=False) btext[0] = mdiff.patch(basetext, delta) try: @@ -2076,8 +2077,8 @@ # full versions are inserted when the needed deltas # become comparable to the uncompressed text if rawtext is None: -textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]), -cachedelta[1]) +# need flags=0 rawtext size, which is the non-raw size +textlen = mdiff.patchedsize(self.size(cachedelta[0]), cachedelta[1]) else: textlen = len(rawtext) To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2067: changegroup: do not delta lfs revisions
quark created this revision. Herald added a reviewer: indygreg. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY There is no way to distinguish whether a delta base is LFS or non-LFS. If the delta is against LFS rawtext, and the client trying to apply it has the base revision stored as fulltext, the delta (aka. bundle) will fail to apply. This patch forbids using delta for LFS revisions in changegroup so bad deltas won't be transmitted. Note: this does not solve the problem entirely. It solves LFS delta applying to non-LFS base. But the other direction: non-LFS delta applying to LFS base is not solved yet. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2067 AFFECTED FILES mercurial/changegroup.py mercurial/revlog.py tests/test-lfs-bundle.t tests/test-lfs.t CHANGE DETAILS diff --git a/tests/test-lfs.t b/tests/test-lfs.t --- a/tests/test-lfs.t +++ b/tests/test-lfs.t @@ -349,7 +349,7 @@ uncompressed size of bundle content: * (changelog) (glob) * (manifests) (glob) - * a (glob) + * a (glob) $ hg --config extensions.strip= strip -r 2 --no-backup --force -q $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a 5 branching diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -96,6 +96,6 @@ 2 integrity errors encountered! (first damaged changeset appears to be 2) Applying src-lfs.bundle to dst-normal - CRASHED + OK Applying src-lfs.bundle to dst-lfs OK diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -713,6 +713,13 @@ except KeyError: return False +def candelta(self, baserev, rev): +"""whether two revisions (prev, rev) can be delta-ed or not""" +# disable delta if either rev uses non-default flag (ex. LFS) +if self.flags(baserev) or self.flags(rev): +return False +return True + def clearcaches(self): self._cache = None self._chainbasecache.clear() diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -770,6 +770,8 @@ progress(msgbundling, None) def deltaparent(self, revlog, rev, p1, p2, prev): +if not revlog.candelta(prev, rev): +raise error.ProgrammingError('cg1 should not be used in this case') return prev def revchunk(self, revlog, rev, prev, linknode): @@ -829,16 +831,19 @@ # expensive. The revlog caches should have prev cached, meaning # less CPU for changegroup generation. There is likely room to add # a flag and/or config option to control this behavior. -return prev +base = prev elif dp == nullrev: # revlog is configured to use full snapshot for a reason, # stick to full snapshot. -return nullrev +base = nullrev elif dp not in (p1, p2, prev): # Pick prev when we can't be sure remote has the base revision. return prev else: -return dp +base = dp +if base != nullrev and not revlog.candelta(base, rev): +base = nullrev +return base def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): # Do nothing with flags, it is implicitly 0 in cg1 and cg2 To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2066: lfs: add a test showing bundle application could be broken
quark created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY When a bundle containing LFS delta uses non-LFS delta-base, or vice-versa, the bundle will fail to apply. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2066 AFFECTED FILES tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t new file mode 100644 --- /dev/null +++ b/tests/test-lfs-bundle.t @@ -0,0 +1,101 @@ +In this test, we want to test LFS bundle application on both LFS and non-LFS +repos. + +To make it more interesting, the file revisions will contain hg filelog +metadata ('\1\n'). The bundle will have 1 file revision overlapping with the +destination repo. + +# rev 1 2 3 +# repo:yesyes no +# bundle: no (base) yes yes (deltabase: 2 if possible) + +It is interesting because rev 2 could have been stored as LFS in the repo, and +non-LFS in the bundle; or vice-versa. + +Init: + + $ cat >> $HGRCPATH << EOF + > [extensions] + > lfs= + > drawdag=$TESTDIR/drawdag.py + > [lfs] + > url=file://$TESTTMP/lfs-remote + > EOF + +Helper functions to create commits: + + $ commitxy() { + > hg debugdrawdag "$@" <<'EOS' + > Y # Y/X=\1\n\nE\nF + > | # Y/Y=\1\n\nG\nH + > X # X/X=\1\n\nC\n + > # X/Y=\1\n\nD\n + > EOS + > } + + $ commitz() { + > hg debugdrawdag "$@" <<'EOS' + > Z # Z/X=\1\n\nI\n + > | # Z/Y=\1\n\nJ\n + > | # Z/Z=\1\nZ + > Y + > EOS + > } + + $ enablelfs() { + > cat >> .hg/hgrc < [lfs] + > threshold=1 + > EOF + > } + +Generate bundles + + $ for i in normal lfs; do + > NAME=src-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > commitz + > hg bundle -q --base X -r Y+Z $TESTTMP/$NAME.bundle + > SRCNAMES="$SRCNAMES $NAME" + > done + +Prepare destination repos + + $ for i in normal lfs; do + > NAME=dst-$i + > hg init $TESTTMP/$NAME + > cd $TESTTMP/$NAME + > [ $i = lfs ] && enablelfs + > commitxy + > DSTNAMES="$DSTNAMES $NAME" + > done + +Apply bundles + + $ cd $TESTTMP + $ for i in $SRCNAMES; do + > for j in $DSTNAMES; do + > echo Applying $i.bundle to $j + > cp -R $TESTTMP/$j $TESTTMP/tmp-$i-$j + > cd $TESTTMP/tmp-$i-$j + > if hg unbundle $TESTTMP/$i.bundle -q 2>/dev/null; then + > hg verify -q && echo OK + > else + > echo CRASHED + > fi + > done + > done + Applying src-normal.bundle to dst-normal + OK + Applying src-normal.bundle to dst-lfs + X@2: unpacking 0609652b7877: integrity check failed on data/X.i:2 + Y@2: unpacking e384812cdeb9: integrity check failed on data/Y.i:2 + 2 integrity errors encountered! + (first damaged changeset appears to be 2) + Applying src-lfs.bundle to dst-normal + CRASHED + Applying src-lfs.bundle to dst-lfs + OK To: quark, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2069: revlog: resolve lfs rawtext to vanilla rawtext before applying delta
quark created this revision. Herald added a reviewer: indygreg. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This happens when a LFS delta base gets a non-LFS delta from another client. In that case, the LFS delta base needs to be converted to non-LFS version before applying the delta. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2069 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t CHANGE DETAILS diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -91,7 +91,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - CRASHED + OK Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -333,7 +333,9 @@ len(delta) - hlen): btext[0] = delta[hlen:] else: -basetext = revlog.revision(baserev, _df=fh, raw=True) +# Deltas are against "flags=0 rawtext".Need "flags=0" rawtext +# here, which is equivalent to non-raw text. +basetext = revlog.revision(baserev, _df=fh, raw=False) btext[0] = mdiff.patch(basetext, delta) try: @@ -2076,8 +2078,8 @@ # full versions are inserted when the needed deltas # become comparable to the uncompressed text if rawtext is None: -textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]), -cachedelta[1]) +# need flags=0 rawtext size, which is the non-raw size +textlen = mdiff.patchedsize(self.size(cachedelta[0]), cachedelta[1]) else: textlen = len(rawtext) To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2068: revlog: do not use delta for lfs revisions
quark created this revision. Herald added a reviewer: indygreg. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This is similar to what we have done for changegroups. It is needed to make sure the delta application code path can assume deltas are always against vanilla (ex. non-LFS) rawtext so the next fix becomes possible. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2068 AFFECTED FILES mercurial/revlog.py tests/test-lfs-bundle.t tests/test-revlog-raw.py CHANGE DETAILS diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py --- a/tests/test-revlog-raw.py +++ b/tests/test-revlog-raw.py @@ -114,6 +114,8 @@ else: # suboptimal deltaparent deltaparent = min(0, parentrev) +if not rlog.candelta(deltaparent, r): +deltaparent = -1 return {'node': rlog.node(r), 'p1': pnode, 'p2': node.nullid, 'cs': rlog.node(rlog.linkrev(r)), 'flags': rlog.flags(r), 'deltabase': rlog.node(deltaparent), @@ -151,12 +153,12 @@ for r in rlog: p1 = rlog.node(r - 1) p2 = node.nullid -if r == 0: +if r == 0 or rlog.flags(r): text = rlog.revision(r, raw=True) cachedelta = None else: -# deltaparent is more interesting if it has the EXTSTORED flag. -deltaparent = max([0] + [p for p in range(r - 2) if rlog.flags(p)]) +# deltaparent cannot have EXTSTORED flag. +deltaparent = max([-1] + [p for p in range(r) if not rlog.flags(p)]) text = None cachedelta = (deltaparent, rlog.revdiff(deltaparent, r)) flags = rlog.flags(r) @@ -262,8 +264,9 @@ result.append((text, rawtext)) # Verify flags like isdelta, isext work as expected -if bool(rlog.deltaparent(rev) > -1) != isdelta: -abort('rev %d: isdelta is ineffective' % rev) +# isdelta can be overridden to False if this or p1 has isext set +if bool(rlog.deltaparent(rev) > -1) and not isdelta: +abort('rev %d: isdelta is unexpected' % rev) if bool(rlog.flags(rev)) != isext: abort('rev %d: isext is ineffective' % rev) return result diff --git a/tests/test-lfs-bundle.t b/tests/test-lfs-bundle.t --- a/tests/test-lfs-bundle.t +++ b/tests/test-lfs-bundle.t @@ -91,10 +91,7 @@ Applying src-normal.bundle to dst-normal OK Applying src-normal.bundle to dst-lfs - X@2: unpacking 0609652b7877: integrity check failed on data/X.i:2 - Y@2: unpacking e384812cdeb9: integrity check failed on data/Y.i:2 - 2 integrity errors encountered! - (first damaged changeset appears to be 2) + CRASHED Applying src-lfs.bundle to dst-normal OK Applying src-lfs.bundle to dst-lfs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -404,6 +404,9 @@ for candidaterevs in self._getcandidaterevs(p1, p2, cachedelta): nominateddeltas = [] for candidaterev in candidaterevs: +# do not use flags != 0 (ex. LFS) revision as delta base +if revlog.flags(candidaterev) != REVIDX_DEFAULT_FLAGS: +continue candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) if revlog._isgooddeltainfo(candidatedelta, revinfo.textlen): nominateddeltas.append(candidatedelta) @@ -2082,7 +2085,12 @@ deltacomputer = _deltacomputer(self) revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags) -deltainfo = deltacomputer.finddeltainfo(revinfo, fh) + +# do not use delta for flags != 0 (ex. LFS) revisions +if flags == REVIDX_DEFAULT_FLAGS: +deltainfo = deltacomputer.finddeltainfo(revinfo, fh) +else: +deltainfo = None if deltainfo is not None: base = deltainfo.base To: quark, indygreg, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D375: simplemerge: write merge result to the localctx, if passed
quark added a comment. https://bz.mercurial-scm.org/5743 bisects to this change. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D375 To: phillco, #hg-reviewers Cc: quark, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2013: commit: allow --no-secret to override phases.new-commit setting
martinvonz added a comment. In https://phab.mercurial-scm.org/D2013#34320, @spectral wrote: > In https://phab.mercurial-scm.org/D2013#33867, @martinvonz wrote: > > > I wonder if we should instead have a --draft option for this. Reasons: > > > > - If we ever add a fourth phase (like Jun's proposed "archived" phase), then --no-secret doesn't clearly indicate "draft", it could just as well be "archived". > > - Actually, we of course already do have a third phase. One could imagine a "hg commit --public", although that's probably not useful enough to warrant its own option, but it seems to suggest that "--no-secret" doesn't necessarily mean "draft". > > - I find this tri-state boolean weird. "--secret" kind of defaults to off, but it can be made "more off" with "--no-secret". > > > Yeah, I wasn't sure I liked it when writing it, and I'm fine with changing it, but do we really want a proliferation of flags? Perhaps we want a generic 'phase' flag, so one can specify -s or --secret (BC and it's the "most common" case), and --phasefor more advanced use-cases? This runs into a couple small problems, specifically that there's now "more than one way to do it [specify secret]", it's a lot of typing, and I don't think we should abbreviate it `-p` (it just doesn't feel like it's going to be common enough to warrant any abbreviation, let alone 'p', which could stand for phase, or patch, or path, or a number of other words that we might eventually add to `commit` and be sad about not being able to abbreviate in the obvious fashion) I think "--phase " makes sense, but it is a little long as you said. Since the real goal of this series is to preserve the phase on `hg split`, I'll see if I can do that by adding general support to scmutil.cleanupnodes() instead (Kyle and I already talked about this offline). REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2013 To: spectral, #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 1 of 2] patches: move assignment outside the conditional
On Tue, Feb 6, 2018 at 6:28 AM, Boris Feldwrote: > # HG changeset patch > # User Boris Feld > # Date 1517923456 -3600 > # Tue Feb 06 14:24:16 2018 +0100 > # Node ID eee9cca843bc557274945688ea52e16539d45e67 > # Parent 1a3e6239a2eb841a5dce5e1cf8212c54bf33 > # EXP-Topic parallel-patching > # Available At https://bitbucket.org/octobus/mercurial-devel/ > # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r > eee9cca843bc > patches: move assignment outside the conditional > > Having this movement in its own patch will make the next patch clearer. > Queued part 1. > > diff --git a/mercurial/cext/mpatch.c b/mercurial/cext/mpatch.c > --- a/mercurial/cext/mpatch.c > +++ b/mercurial/cext/mpatch.c > @@ -110,7 +110,8 @@ patches(PyObject *self, PyObject *args) > goto cleanup; > } > out = PyBytes_AsString(result); > - if ((r = mpatch_apply(out, in, inlen, patch)) < 0) { > + r = mpatch_apply(out, in, inlen, patch); > + if (r < 0) { > Py_DECREF(result); > result = NULL; > } > ___ > 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 2 of 2] patches: release the GIL while applying the patch
On Tue, Feb 6, 2018 at 6:28 AM, Boris Feldwrote: > # HG changeset patch > # User Boris Feld > # Date 1517839431 -3600 > # Mon Feb 05 15:03:51 2018 +0100 > # Node ID 345f1c897538cd2b0a5a2fd3952b880206fd9339 > # Parent eee9cca843bc557274945688ea52e16539d45e67 > # EXP-Topic parallel-patching > # Available At https://bitbucket.org/octobus/mercurial-devel/ > # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r > 345f1c897538 > patches: release the GIL while applying the patch > > This will allow multiple threads to apply patches at the same time. > Please use Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS macros, as that is what the Python docs recommend. I'm not sure why we don't do this consistently in our code today. IMO we should port existing code to these macros. > > diff --git a/mercurial/cext/mpatch.c b/mercurial/cext/mpatch.c > --- a/mercurial/cext/mpatch.c > +++ b/mercurial/cext/mpatch.c > @@ -77,6 +77,7 @@ patches(PyObject *self, PyObject *args) > int r = 0; > char *out; > Py_ssize_t len, outlen, inlen; > + PyThreadState *_save; > > if (!PyArg_ParseTuple(args, "OO:mpatch", , )) > return NULL; > @@ -110,7 +111,9 @@ patches(PyObject *self, PyObject *args) > goto cleanup; > } > out = PyBytes_AsString(result); > + _save = PyEval_SaveThread(); > r = mpatch_apply(out, in, inlen, patch); > + PyEval_RestoreThread(_save); > if (r < 0) { > Py_DECREF(result); > result = NULL; > ___ > 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
D2065: wireprotoserver: rename abstractserverproto and improve docstring
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The docstring isn't completely accurate for the current state of the world. But it does describe the direction future patches will be taking things. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2065 AFFECTED FILES mercurial/wireprotoserver.py CHANGE DETAILS diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py +++ b/mercurial/wireprotoserver.py @@ -32,10 +32,13 @@ HGTYPE2 = 'application/mercurial-0.2' HGERRTYPE = 'application/hg-error' -class abstractserverproto(object): -"""abstract class that summarizes the protocol API +class baseprotocolhandler(object): +"""Abstract base class for wire protocol handlers. -Used as reference and documentation. +A wire protocol handler serves as an interface between protocol command +handlers and the wire protocol transport layer. Protocol handlers provide +methods to read command arguments, redirect stdio for the duration of +the request, handle response types, etc. """ __metaclass__ = abc.ABCMeta @@ -98,7 +101,7 @@ return ''.join(chunks) -class webproto(abstractserverproto): +class webproto(baseprotocolhandler): def __init__(self, req, ui): self._req = req self._ui = ui @@ -327,7 +330,7 @@ return '' -class sshserver(abstractserverproto): +class sshserver(baseprotocolhandler): def __init__(self, ui, repo): self._ui = ui self._repo = repo 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
D2064: wireprotoserver: document and improve the httplib workaround
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY This workaround dates all the way back to https://phab.mercurial-scm.org/rHGa42d27bc809dda4939c5b3807b397bcc811ebbe0 in 2008. The code is esoteric enough to warrant an inline explanation. So I've added one. At the time the code was written, the only wire protocol command that accepted an HTTP request body was "unbundle." In the years since, we've grown the ability to accept command arguments via HTTP POST requests. So, the code has been changed to apply the httplib workaround to all HTTP POST requests. While staring at this code, I realized that the HTTP response body in case of error is always the same. And, it appears to mimic the behavior of a failed call to the "unbundle" command. Since we can hit this code path on theoretically any protocol request (since self.check_perm accepts custom auth checking functions which may raise), I'm having a hard time believing that clients react well to an "unbundle" response payload on any wire protocol command. I wouldn't be surprised if our test coverage for this feature only covers HTTP POST calls to "unbundle." In other words, the experimental support for sending arguments via HTTP POST request bodies may result in badness on the client. Something to investigate another time perhaps... REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2064 AFFECTED FILES mercurial/wireprotoserver.py CHANGE DETAILS diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py +++ b/mercurial/wireprotoserver.py @@ -286,8 +286,9 @@ req.respond(HTTP_OK, HGTYPE, body=rsp) return [] elif isinstance(rsp, wireproto.pusherr): -# drain the incoming bundle +# This is the httplib workaround documented in _handlehttperror(). req.drain() + proto.restore() rsp = '0\n%s\n' % rsp.res req.respond(HTTP_OK, HGTYPE, body=rsp) @@ -300,16 +301,28 @@ def _handlehttperror(e, req, cmd): """Called when an ErrorResponse is raised during HTTP request processing.""" -# A client that sends unbundle without 100-continue will -# break if we respond early. -if (cmd == 'unbundle' and + +# Clients using Python's httplib are stateful: the HTTP client +# won't process an HTTP response until all request data is +# sent to the server. The intent of this code is to ensure +# we always read HTTP request data from the client, thus +# ensuring httplib transitions to a state that allows it to read +# the HTTP response. In other words, it helps prevent deadlocks +# on clients using httplib. + +if (req.env[r'REQUEST_METHOD'] == r'POST' and +# But not if Expect: 100-continue is being used. (req.env.get('HTTP_EXPECT', '').lower() != '100-continue') or +# Or the non-httplib HTTP library is being advertised by +# the client. req.env.get('X-HgHttp2', '')): req.drain() else: req.headers.append((r'Connection', r'Close')) +# TODO This response body assumes the failed command was +# "unbundle." That assumption is not always valid. req.respond(e, HGTYPE, body='0\n%s\n' % e) return '' 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
D2021: wireprotoserver: move error response handling out of hgweb
indygreg updated this revision to Diff 5260. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2021?vs=5175=5260 REVISION DETAIL https://phab.mercurial-scm.org/D2021 AFFECTED FILES mercurial/hgweb/hgweb_mod.py mercurial/wireprotoserver.py CHANGE DETAILS diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py +++ b/mercurial/wireprotoserver.py @@ -236,6 +236,7 @@ 'cmd': cmd, 'proto': proto, 'dispatch': lambda: _callhttp(repo, req, proto, cmd), +'handleerror': lambda ex: _handlehttperror(ex, req, cmd), } def _callhttp(repo, req, proto, cmd): @@ -297,6 +298,22 @@ return [] raise error.ProgrammingError('hgweb.protocol internal failure', rsp) +def _handlehttperror(e, req, cmd): +"""Called when an ErrorResponse is raised during HTTP request processing.""" +# A client that sends unbundle without 100-continue will +# break if we respond early. +if (cmd == 'unbundle' and +(req.env.get('HTTP_EXPECT', + '').lower() != '100-continue') or +req.env.get('X-HgHttp2', '')): +req.drain() +else: +req.headers.append((r'Connection', r'Close')) + +req.respond(e, HGTYPE, body='0\n%s\n' % e) + +return '' + class sshserver(abstractserverproto): def __init__(self, ui, repo): self._ui = ui diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -369,18 +369,7 @@ if cmd in perms: self.check_perm(rctx, req, perms[cmd]) except ErrorResponse as inst: -# A client that sends unbundle without 100-continue will -# break if we respond early. -if (cmd == 'unbundle' and -(req.env.get('HTTP_EXPECT', - '').lower() != '100-continue') or -req.env.get('X-HgHttp2', '')): -req.drain() -else: -req.headers.append((r'Connection', r'Close')) -req.respond(inst, wireprotoserver.HGTYPE, -body='0\n%s\n' % inst) -return '' +return protohandler['handleerror'](inst) return protohandler['dispatch']() 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
D2019: wireprotoserver: move protocol parsing and dispatch out of hgweb
indygreg updated this revision to Diff 5259. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2019?vs=5173=5259 REVISION DETAIL https://phab.mercurial-scm.org/D2019 AFFECTED FILES mercurial/hgweb/hgweb_mod.py mercurial/wireprotoserver.py CHANGE DETAILS diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py +++ b/mercurial/wireprotoserver.py @@ -202,9 +202,43 @@ def iscmd(cmd): return cmd in wireproto.commands -def callhttp(repo, req, cmd): +def parsehttprequest(repo, req, query): +"""Parse the HTTP request for a wire protocol request. + +If the current request appears to be a wire protocol request, this +function returns a dict with details about that request, including +an ``abstractprotocolserver`` instance suitable for handling the +request. Otherwise, ``None`` is returned. + +``req`` is a ``wsgirequest`` instance. +""" +# HTTP version 1 wire protocol requests are denoted by a "cmd" query +# string parameter. If it isn't present, this isn't a wire protocol +# request. +if r'cmd' not in req.form: +return None + +cmd = pycompat.sysbytes(req.form[r'cmd'][0]) + +# The "cmd" request parameter is used by both the wire protocol and hgweb. +# While not all wire protocol commands are available for all transports, +# if we see a "cmd" value that resembles a known wire protocol command, we +# route it to a protocol handler. This is better than routing possible +# wire protocol requests to hgweb because it prevents hgweb from using +# known wire protocol commands and it is less confusing for machine +# clients. +if cmd not in wireproto.commands: +return None + proto = webproto(req, repo.ui) +return { +'cmd': cmd, +'proto': proto, +'dispatch': lambda: _callhttp(repo, req, proto, cmd), +} + +def _callhttp(repo, req, proto, cmd): def genversion2(gen, engine, engineopts): # application/mercurial-0.2 always sends a payload header # identifying the compression engine. diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -357,26 +357,18 @@ query = req.env[r'QUERY_STRING'].partition(r'&')[0] query = query.partition(r';')[0] -# The ``cmd`` request parameter is used by both the wire protocol -# and hgweb. We route all known wire protocol commands to the -# wire protocol handler, even if the command isn't available for -# this transport. That's better for machine clients in the case -# of an errant request to an unavailable protocol command. And it -# prevents hgweb from accidentally using ``cmd`` values used by -# the wire protocol. +# Route it to a wire protocol handler if it looks like a wire protocol +# request. +protohandler = wireprotoserver.parsehttprequest(rctx.repo, req, query) -# process this if it's a protocol request -# protocol bits don't need to create any URLs -# and the clients always use the old URL structure - -cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0]) -if wireprotoserver.iscmd(cmd): +if protohandler: +cmd = protohandler['cmd'] try: if query: raise ErrorResponse(HTTP_NOT_FOUND) if cmd in perms: self.check_perm(rctx, req, perms[cmd]) -return wireprotoserver.callhttp(rctx.repo, req, cmd) +return protohandler['dispatch']() except ErrorResponse as inst: # A client that sends unbundle without 100-continue will # break if we respond early. @@ -425,6 +417,8 @@ if fn.endswith(ext): req.form['node'] = [fn[:-len(ext)]] req.form['type'] = [type_] +else: +cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0]) # process the web interface request 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
D1999: wireproto: function for testing if wire protocol command is available
indygreg updated this revision to Diff 5258. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D1999?vs=5138=5258 REVISION DETAIL https://phab.mercurial-scm.org/D1999 AFFECTED FILES mercurial/hgweb/hgweb_mod.py mercurial/wireproto.py mercurial/wireprotoserver.py CHANGE DETAILS diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py +++ b/mercurial/wireprotoserver.py @@ -217,6 +217,13 @@ yield chunk rsp = wireproto.dispatch(repo, proto, cmd) + +if not wireproto.commands.commandavailable(cmd, proto): +req.respond(HTTP_OK, HGERRTYPE, +body=_('requested wire protocol command is not available ' + 'over HTTP')) +return [] + if isinstance(rsp, bytes): req.respond(HTTP_OK, HGTYPE, body=rsp) return [] @@ -345,7 +352,7 @@ def serve_one(self): cmd = self._fin.readline()[:-1] -if cmd and cmd in wireproto.commands: +if cmd and wireproto.commands.commandavailable(cmd, self): rsp = wireproto.dispatch(self._repo, self, cmd) self._handlers[rsp.__class__](self, rsp) elif cmd: diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -691,6 +691,12 @@ return super(commanddict, self).__setitem__(k, v) +def commandavailable(self, command, proto): +"""Determine if a command is available for the requested protocol.""" +# For now, commands are available for all protocols. So do a simple +# membership test. +return command in self + commands = commanddict() def wireprotocommand(name, args=''): diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -357,6 +357,14 @@ query = req.env[r'QUERY_STRING'].partition(r'&')[0] query = query.partition(r';')[0] +# The ``cmd`` request parameter is used by both the wire protocol +# and hgweb. We route all known wire protocol commands to the +# wire protocol handler, even if the command isn't available for +# this transport. That's better for machine clients in the case +# of an errant request to an unavailable protocol command. And it +# prevents hgweb from accidentally using ``cmd`` values used by +# the wire protocol. + # process this if it's a protocol request # protocol bits don't need to create any URLs # and the clients always use the old URL structure 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
D2057: translate base85.c into rust code
indygreg added a comment. Yes, we should definitely split things into multiple crates. Small, narrowly-focused crates does seem to be the Rust way, after all. `hgcli` should be for things specific to the Rust implementation of `hg`. I think this can also include the feature set of `chg` (once we've ported `chg` to Rust). I definitely support separating the "pure Rust" from the "Python Rust" via a crate boundary. It is generally useful to have Rust that isn't bound to Python because it will facilitate reuse outside of Python contexts. For example, someone could implement a Mercurial wire protocol server in pure Rust without needing to worry about Python. Of course, we're likely to encounter areas where we really want tight coupling in order to achieve optimal performance in Python. So we may have to design APIs on the pure Rust side to facilitate CPython use. I'm OK with that. As for how many crates to have, I don't have super strong opinions. I could see us putting every little component/subsystem in its own crate. I could also see us putting everything in one large crate. I don't think it is worth deciding at this early juncture. API design and ability to be reused outside its originally intended purpose is the important property to strive for. I think that has more to do with how the code is authored rather than which crates things are in. A missing piece of this patch is the build system and module loader integration. We have a //module policy// that dictates which implementation of a Python module we use. We probably want to introduce a `rust` policy that uses Rust-based modules where available and falls back to the `cext` modules/policy if a Rust module isn't available. We also need to figure out how to integrate Rust into `setup.py`. But I think the build system bit can be deferred until we're actually ready to ship Rust, which is still a bit of ways off. I'm happy for the workflow to be //run cargo in order to load Rust modules// for the time being. But if you can implement `Makefile` and/or `setup.py` integration to build these Rust extensions, that would be awesome. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2061: sshpeer: initial definition and implementation of new SSH protocol
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The existing SSH protocol has several design flaws. Future commits will elaborate on these flaws as new features are introduced to combat these flaws. For now, hopefully you can take me for my word that a ground up rewrite of the SSH protocol is needed. This commit lays the foundation for a new SSH protocol by defining a mechanism to upgrade the SSH transport channel away from the default (version 1) protocol to something modern (which we'll call "version 2" for now). This upgrade process is detailed in the internals documentation for the wire protocol. The gist of it is the client sends a request line preceding the "hello" command/line which basically says "I'm requesting an upgrade: here's what I support." If the server recognizes that line, it processes the upgrade request and the transport channel is switched to use the new version of the protocol. If not, it sends an empty response, which is how all Mercurial SSH servers from the beginning of time reacted to unknown commands. The upgrade request is effectively ignored and the client continues to use the existing version of the protocol as if nothing happened. The new version of the SSH protocol is completely identical to version 1 aside from the upgrade dance and the bytes that follow. The immediate bytes that follow the protocol switch are defined to be a length framed "capabilities: " line containing the remote's advertised capabilities. In reality, this looks very similar to what the "hello" response would look like. But it will evolve quickly. The methodology by which the protocol will evolve is important. I'm not going to introduce the new protocol all at once. That would likely lead to endless bike shedding and forward progress would stall. Instead, I intend to tricle out new features and diversions from the existing protocol in small, incremental changes. To support the gradual evolution of the protocol, the on-the-wire advertised protocol name contains an "exp" to denote "experimental" and a 4 digit field to capture the sub-version of the protocol. Whenever we make a BC change to the wire protocol, we can increment this version and lock out all older clients because it will appear as a completely different protocol version. This means we can incur as many breaking changes as we want. We don't have to commit to supporting any one feature or idea for a long period of time. We can even evolve the handshake mechanism, because that is defined as being an implementation detail of the negotiated protocol version! Hopefully this lowers the barrier to accepting changes to the protocol and for experimenting with "radical" ideas during its development. In core, sshpeer received most of the attention. We haven't even implemented the server bits for the new protocol in core yet. Instead, we add very primitive support to our test server, mainly just to exercise the added code paths in sshpeer. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2061 AFFECTED FILES mercurial/configitems.py mercurial/help/internals/wireprotocol.txt mercurial/sshpeer.py mercurial/wireprotoserver.py tests/sshprotoext.py tests/test-ssh-proto.t CHANGE DETAILS diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t +++ b/tests/test-ssh-proto.t @@ -388,3 +388,107 @@ 0 0 0 + +Send an upgrade request to a server that doesn't support that command + + $ hg -R server serve --stdio << EOF + > upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=irrelevant1%2Cirrelevant2 + > hello + > between + > pairs 81 + > - + > EOF + 0 + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + 1 + + + $ hg --config experimental.sshpeer.advertise-v2=true --debug debugpeer ssh://user@dummy/server + running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) + sending upgrade request: * proto=exp-ssh-v2-0001 (glob) + devel-peer-request: hello + sending hello command + devel-peer-request: between + devel-peer-request: pairs: 81 bytes + sending between command + remote: 0 + remote: 384 + remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + remote: 1 + url: ssh://user@dummy/server + local: no + pushable: yes + +Send an upgrade request to a server that supports upgrade + + $ SSHSERVERMODE=upgradev2 hg -R server serve --stdio << EOF + > upgrade this-is-some-token
D2062: sshpeer: rename sshpeer class to sshv1peer (API)
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY With the introduction of version 2 of the SSH wire protocol, we will need a new peer class to speak that protocol because it will be too difficult to shoehorn a single class to speak two protocols. We rename sshpeer.sshpeer to sshpeer.sshv1peer to reflect the fact that there will be multiple versions of the peer depending on the negotiated protocol. .. api:: sshpeer.sshpeer renamed to sshpeer.sshv1peer. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2062 AFFECTED FILES hgext/largefiles/uisetup.py mercurial/sshpeer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -65,8 +65,8 @@ checkobject(badpeer()) checkobject(httppeer.httppeer(ui, 'http://localhost')) checkobject(localrepo.localpeer(dummyrepo())) -checkobject(sshpeer.sshpeer(ui, 'ssh://localhost/foo', None, None, None, - None, None)) +checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, None, None, + None, None)) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -328,7 +328,7 @@ return caps -class sshpeer(wireproto.wirepeer): +class sshv1peer(wireproto.wirepeer): def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): """Create a peer from an existing SSH connection. @@ -537,4 +537,4 @@ _cleanuppipes(ui, stdout, stdin, stderr) raise -return sshpeer(ui, path, proc, stdin, stdout, stderr, caps) +return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps) diff --git a/hgext/largefiles/uisetup.py b/hgext/largefiles/uisetup.py --- a/hgext/largefiles/uisetup.py +++ b/hgext/largefiles/uisetup.py @@ -185,9 +185,9 @@ # can't do this in reposetup because it needs to have happened before # wirerepo.__init__ is called -proto.ssholdcallstream = sshpeer.sshpeer._callstream +proto.ssholdcallstream = sshpeer.sshv1peer._callstream proto.httpoldcallstream = httppeer.httppeer._callstream -sshpeer.sshpeer._callstream = proto.sshrepocallstream +sshpeer.sshv1peer._callstream = proto.sshrepocallstream httppeer.httppeer._callstream = proto.httprepocallstream # override some extensions' stuff as well 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
D2060: internals: refactor wire protocol documentation
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Upcoming work will introduce a new version of the HTTP and SSH transports. The differences will be significant enough to consider them new transports. So, we now attach a version number to each transport. In addition, having the handshake documented after the transport and in a single shared section made it harder to follow the flow of the connection. The handshake documentation is now moved to the protocol section it describes. We now have a generic section about the purpose of the handshake, which was rewritten significantly. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2060 AFFECTED FILES mercurial/help/internals/wireprotocol.txt CHANGE DETAILS diff --git a/mercurial/help/internals/wireprotocol.txt b/mercurial/help/internals/wireprotocol.txt --- a/mercurial/help/internals/wireprotocol.txt +++ b/mercurial/help/internals/wireprotocol.txt @@ -10,11 +10,43 @@ The protocol is synchronous and does not support multiplexing (concurrent commands). -Transport Protocols -=== +Handshake += + +It is required or common for clients to perform a *handshake* when connecting +to a server. The handshake serves the following purposes: + +* Negotiating protocol/transport level options +* Allows the client to learn about server capabilities to influence + future requests +* Ensures the underlying transport channel is in a *clean* state -HTTP Transport --- +An important goal of the handshake is to allow clients to use more modern +wire protocol features. By default, clients must assume they are talking +to an old version of Mercurial server (possibly even the very first +implementation). So, clients should not attempt to call or utilize modern +wire protocol features until they have confirmation that the server +supports them. The handshake implementation is designed to allow both +ends to utilize the latest set of features and capabilities with as +few round trips as possible. + +The handshake mechanism varies by transport and protocol and is documented +in the sections below. + +HTTP Protocol += + +Handshake +- + +The client sends a ``capabilities`` command request (``?cmd=capabilities``) +as soon as HTTP requests may be issued. + +The server responds with a capabilities string, which the client parses to +learn about the server's abilities. + +HTTP Version 1 Transport + Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are sent to the base URL of the repository with the command name sent in @@ -112,11 +144,86 @@ ``application/mercurial-0.*`` media type and the HTTP response is typically using *chunked transfer* (``Transfer-Encoding: chunked``). -SSH Transport -= +SSH Protocol + + +Handshake +- + +For all clients, the handshake consists of the client sending 1 or more +commands to the server using version 1 of the transport. Servers respond +to commands they know how to respond to and send an empty response (``0\n``) +for unknown commands (per standard behavior of version 1 of the transport). +Clients then typically look for a response to the newest sent command to +determine which transport version to use and what the available features for +the connection and server are. + +Preceding any response from client-issued commands, the server may print +non-protocol output. It is common for SSH servers to print banners, message +of the day announcements, etc when clients connect. It is assumed that any +such *banner* output will precede any Mercurial server output. So clients +must be prepared to handle server output on initial connect that isn't +in response to any client-issued command and doesn't conform to Mercurial's +wire protocol. This *banner* output should only be on stdout. However, +some servers may send output on stderr. + +Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument +having the value +``-``. + +The ``between`` command has been supported since the original Mercurial +SSH server. Requesting the empty range will return a ``\n`` string response, +which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline +followed by the value, which happens to be a newline). + +For pre 0.9.1 clients and all servers, the exchange looks like:: + + c: between\n + c: pairs 81\n + c: - + s: 1\n + s: \n -The SSH transport is a custom text-based protocol suitable for use over any -bi-directional stream transport. It is most commonly used with SSH. +0.9.1+ clients send a ``hello`` command (with no arguments) before the +``between`` command. The response to this command allows clients to
D2063: sshpeer: implement peer for version 2 of wire protocol
indygreg created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Since the protocol is now negotiated before we construct a peer instance, we can return the negotiated protocol from the handshake function and instantiate an appropriate peer class for the protocol. Version 2 of the SSH protocol is currently identical to version 1 post handshake. So our sshv2peer class just inherits from sshv1peer for the time being. This will obviously change over time. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2063 AFFECTED FILES mercurial/sshpeer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -67,6 +67,8 @@ checkobject(localrepo.localpeer(dummyrepo())) checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, None, None, None, None)) +checkobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, None, None, + None, None)) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -326,7 +326,7 @@ if not caps: badresponse() -return caps +return protoname, caps class sshv1peer(wireproto.wirepeer): def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): @@ -497,6 +497,12 @@ self._pipeo.flush() self._readerr() +class sshv2peer(sshv1peer): +"""A peer that speakers version 2 of the transport protocol.""" +# Currently version 2 is identical to version 1 post handshake. +# And handshake is performed before the peer is instantiated. So +# we need no custom code. + def instance(ui, path, create): """Create an SSH peer. @@ -532,9 +538,16 @@ remotepath, sshenv) try: -caps = _performhandshake(ui, stdin, stdout, stderr) +protoname, caps = _performhandshake(ui, stdin, stdout, stderr) except Exception: _cleanuppipes(ui, stdout, stdin, stderr) raise -return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps) +if protoname == wireprotoserver.SSHV1: +return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps) +elif protoname == wireprotoserver.SSHV2: +return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps) +else: +_cleanuppipes(ui, stdout, stdin, stderr) +raise error.RepoError(_('unknown version of SSH protocol: %s') % + protoname) 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
D1808: debugcommands: print out the editor that was searched for (post shlexsplit)
krbullock accepted this revision as: krbullock. krbullock added a comment. This LGTM now. I think the executable as interpreted by shlex, but without the added flags, gives enough signal for the user to figure out what might be wrong without confusing things. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1808 To: spectral, #hg-reviewers, krbullock Cc: krbullock, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D1808: debugcommands: print out the editor that was searched for (post shlexsplit)
spectral marked an inline comment as done. spectral added a comment. Sorry, I forgot about this, can you take another look? I believe I addressed your most recent comment. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D1808 To: spectral, #hg-reviewers, krbullock Cc: krbullock, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
mercurial@35920: 6 new changesets
6 new changesets in mercurial: https://www.mercurial-scm.org/repo/hg/rev/83b9f96ce20f changeset: 35915:83b9f96ce20f user:Pulkit Goyal <7895pul...@gmail.com> date:Mon Feb 05 13:01:35 2018 +0530 summary: py3: use "%d" to convert integer to bytes https://www.mercurial-scm.org/repo/hg/rev/d41e41d11574 changeset: 35916:d41e41d11574 user:Pulkit Goyal <7895pul...@gmail.com> date:Mon Feb 05 13:10:33 2018 +0530 summary: py3: add __bytes__() for mq.patchheader and make sure __str__ returns str https://www.mercurial-scm.org/repo/hg/rev/3d8b0020f470 changeset: 35917:3d8b0020f470 user:Pulkit Goyal <7895pul...@gmail.com> date:Mon Feb 05 13:12:01 2018 +0530 summary: py3: use pycompat.strkwargs() to convert kwargs' key to str https://www.mercurial-scm.org/repo/hg/rev/e35616bb6ede changeset: 35918:e35616bb6ede user:Pulkit Goyal <7895pul...@gmail.com> date:Mon Feb 05 13:12:36 2018 +0530 summary: py3: use open() instead of file() https://www.mercurial-scm.org/repo/hg/rev/143d7b27b09c changeset: 35919:143d7b27b09c user:Pulkit Goyal <7895pul...@gmail.com> date:Fri Dec 29 05:40:49 2017 +0530 summary: check-config: specify the mode 'rb' to open the file https://www.mercurial-scm.org/repo/hg/rev/2912bed9b0c7 changeset: 35920:2912bed9b0c7 bookmark:@ tag: tip user:Pulkit Goyal <7895pul...@gmail.com> date:Mon Feb 05 13:24:02 2018 +0530 summary: py3: add b'' to literals in check-config.py -- 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
FYI: Mercurial 4.6 sprint is March 2nd-4th at Google's Cambridge, MA office
https://www.mercurial-scm.org/wiki/4.6sprint has all the details, and I'll fill in more as I have them. Sorry for not doing the usual planning routine on this, but time got away from us and we decided to make some decisions quickly rather than waiting for the usual date-picking routine. See (some) of you next month! Augie ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2057: translate base85.c into rust code
Ivzhh added a comment. I am open to the three-crates plan. Oirginally I have hgcli and hgext separately, and I was planning to replace CFFI. I am a pypy user too, so I will be willing to provide a python C API free crate for pypy and others. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] makefile: remove Ubuntu Yakkety and Zesty docker targets
On Mon, 5 Feb 2018 09:22:02 -0800, Gregory Szorc wrote: > On Mon, Feb 5, 2018 at 5:23 AM, Yuya Nishiharawrote: > > > On Mon, 05 Feb 2018 18:48:00 +0800, Anton Shestakov wrote: > > > # HG changeset patch > > > # User Anton Shestakov > > > # Date 1517827181 -28800 > > > # Mon Feb 05 18:39:41 2018 +0800 > > > # Node ID d5509e242b927dbee808e4d62a7f10b3a5c406cf > > > # Parent ed3a7300b7b5a427bdcffefd0c80aab65824bf4b > > > makefile: remove Ubuntu Yakkety and Zesty docker targets > > > > Queued, thanks. > > > > In the past, I've had to restore support for some old distros in order to > build modern Mercurial packages for some older distros that still had > limited use in the wild. So there is value to keeping these targets around > even if the distro is no longer supported upstream. It's only a minor > inconvenience for me. I just thought I'd raise the concern in case it > influences our decision making on patches like this. I have no preference. It's just a make rule kicking one command. It could even be "make docker-ubuntu CODENAME=yakkety" so we don't have to update our Makefile per Ubuntu release. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 2] patches: move assignment outside the conditional
# HG changeset patch # User Boris Feld# Date 1517923456 -3600 # Tue Feb 06 14:24:16 2018 +0100 # Node ID eee9cca843bc557274945688ea52e16539d45e67 # Parent 1a3e6239a2eb841a5dce5e1cf8212c54bf33 # EXP-Topic parallel-patching # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r eee9cca843bc patches: move assignment outside the conditional Having this movement in its own patch will make the next patch clearer. diff --git a/mercurial/cext/mpatch.c b/mercurial/cext/mpatch.c --- a/mercurial/cext/mpatch.c +++ b/mercurial/cext/mpatch.c @@ -110,7 +110,8 @@ patches(PyObject *self, PyObject *args) goto cleanup; } out = PyBytes_AsString(result); - if ((r = mpatch_apply(out, in, inlen, patch)) < 0) { + r = mpatch_apply(out, in, inlen, patch); + if (r < 0) { Py_DECREF(result); result = NULL; } ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2] patches: release the GIL while applying the patch
# HG changeset patch # User Boris Feld# Date 1517839431 -3600 # Mon Feb 05 15:03:51 2018 +0100 # Node ID 345f1c897538cd2b0a5a2fd3952b880206fd9339 # Parent eee9cca843bc557274945688ea52e16539d45e67 # EXP-Topic parallel-patching # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 345f1c897538 patches: release the GIL while applying the patch This will allow multiple threads to apply patches at the same time. diff --git a/mercurial/cext/mpatch.c b/mercurial/cext/mpatch.c --- a/mercurial/cext/mpatch.c +++ b/mercurial/cext/mpatch.c @@ -77,6 +77,7 @@ patches(PyObject *self, PyObject *args) int r = 0; char *out; Py_ssize_t len, outlen, inlen; + PyThreadState *_save; if (!PyArg_ParseTuple(args, "OO:mpatch", , )) return NULL; @@ -110,7 +111,9 @@ patches(PyObject *self, PyObject *args) goto cleanup; } out = PyBytes_AsString(result); + _save = PyEval_SaveThread(); r = mpatch_apply(out, in, inlen, patch); + PyEval_RestoreThread(_save); if (r < 0) { Py_DECREF(result); result = NULL; ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 7 V2] cmdutil: convert _revertprefetch() to a generic stored file hook (API)
On Tue, 6 Feb 2018 08:02:30 -0500, Matt Harbison wrote: > > On Feb 6, 2018, at 7:20 AM, Yuya Nishiharawrote: > > - prefetchfiles() could be hosted by mergemod so lfs won't need to wrap > > applyupdates(). > > Would it be better to keep it in cmdutil, and call it just before calling > applyupdates()? That wouldn't be possible because the actions are calculated in merge.py. > Calling mergemod.prefetchfiles() from archive, for example, seems weird, > doesn’t it? Or am I misunderstanding? Another option would be repo.prefetchfiles(), but I'm not sure if it's a good idea. > > - it could be a list of callback functions, instead of carefully wrapping > > the function itself > > Just a raw list that everything can access, or should it have add/remove > methods too? I’ve seen both patterns with hook registration, IIRC. I have no preference. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2025: debugcommands: introduce debugpeer command
This revision was automatically updated to reflect the committed changes. Closed by commit rHG5f029d03cf71: debugcommands: introduce debugpeer command (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2025?vs=5183=5242 REVISION DETAIL https://phab.mercurial-scm.org/D2025 AFFECTED FILES mercurial/debugcommands.py tests/test-completion.t tests/test-debugcommands.t 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 @@ -948,6 +948,7 @@ debugoptEXP (no help text available) debugpathcomplete complete part or all of a tracked path + debugpeer establish a connection to a peer repository debugpickmergetool examine which merge tool is chosen for specified file debugpushkey access the pushkey key/value protocol diff --git a/tests/test-debugcommands.t b/tests/test-debugcommands.t --- a/tests/test-debugcommands.t +++ b/tests/test-debugcommands.t @@ -381,3 +381,24 @@ https stream v2 + +Test debugpeer + + $ hg --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" debugpeer ssh://user@dummy/debugrevlog + url: ssh://user@dummy/debugrevlog + local: no + pushable: yes + + $ hg --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" --debug debugpeer ssh://user@dummy/debugrevlog + running "*" "*/tests/dummyssh" 'user@dummy' 'hg -R debugrevlog serve --stdio' (glob) + devel-peer-request: hello + sending hello command + devel-peer-request: between + devel-peer-request: pairs: 81 bytes + sending between command + remote: 384 + remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + remote: 1 + url: ssh://user@dummy/debugrevlog + local: no + pushable: yes diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -102,6 +102,7 @@ debugnamecomplete debugobsolete debugpathcomplete + debugpeer debugpickmergetool debugpushkey debugpvec @@ -281,6 +282,7 @@ debugnamecomplete: debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template debugpathcomplete: full, normal, added, removed + debugpeer: debugpickmergetool: rev, changedelete, include, exclude, tool debugpushkey: debugpvec: diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -1693,6 +1693,25 @@ ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files))) ui.write('\n') +@command('debugpeer', [], _('PATH'), norepo=True) +def debugpeer(ui, path): +"""establish a connection to a peer repository""" +# Always enable peer request logging. Requires --debug to display +# though. +overrides = { +('devel', 'debug.peer-request'): True, +} + +with ui.configoverride(overrides): +peer = hg.peer(ui, {}, path) + +local = peer.local() is not None +canpush = peer.canpush() + +ui.write(_('url: %s\n') % peer.url()) +ui.write(_('local: %s\n') % (_('yes') if local else _('no'))) +ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no'))) + @command('debugpickmergetool', [('r', 'rev', '', _('check for files in this revision'), _('REV')), ('', 'changedelete', None, _('emulate merging change and delete')), To: indygreg, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2036: sshpeer: remove support for connecting to <0.9.1 servers (BC)
This revision was automatically updated to reflect the committed changes. Closed by commit rHG556218e08e25: sshpeer: remove support for connecting to 0.9.1 servers (BC) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2036?vs=5237=5253 REVISION DETAIL https://phab.mercurial-scm.org/D2036 AFFECTED FILES mercurial/sshpeer.py tests/test-ssh-proto.t CHANGE DETAILS diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t +++ b/tests/test-ssh-proto.t @@ -109,7 +109,9 @@ 1 -Connecting to a <0.9.1 server that doesn't support the hello command +Connecting to a <0.9.1 server that doesn't support the hello command. +The client should refuse, as we dropped support for connecting to such +servers. $ SSHSERVERMODE=no-hello hg --debug debugpeer ssh://user@dummy/server running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) @@ -120,14 +122,8 @@ sending between command remote: 0 remote: 1 - url: ssh://user@dummy/server - local: no - pushable: yes - -The client should interpret this as no capabilities - - $ SSHSERVERMODE=no-hello hg debugcapabilities ssh://user@dummy/server - Main capabilities: + abort: no suitable response from remote hg! + [255] Sending an unknown command to the server results in an empty response to that command diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -242,6 +242,16 @@ caps.update(l[:-1].split(':')[1].split()) break +# Error if we couldn't find a response to ``hello``. This could +# mean: +# +# 1. Remote isn't a Mercurial server +# 2. Remote is a <0.9.1 Mercurial server +# 3. Remote is a future Mercurial server that dropped ``hello`` +#support. +if not caps: +badresponse() + return caps class sshpeer(wireproto.wirepeer): To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2034: sshpeer: move handshake outside of sshpeer
This revision was automatically updated to reflect the committed changes. Closed by commit rHG80a2b8ae42a1: sshpeer: move handshake outside of sshpeer (authored by indygreg, committed by ). CHANGED PRIOR TO COMMIT https://phab.mercurial-scm.org/D2034?vs=5235=5251#toc REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2034?vs=5235=5251 REVISION DETAIL https://phab.mercurial-scm.org/D2034 AFFECTED FILES mercurial/sshpeer.py tests/sshprotoext.py tests/test-check-interfaces.py tests/test-ssh-proto.t CHANGE DETAILS diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t +++ b/tests/test-ssh-proto.t @@ -146,7 +146,6 @@ $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-no-args --debug debugpeer ssh://user@dummy/server running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) - devel-peer-request: no-args sending no-args command devel-peer-request: hello sending hello command @@ -182,11 +181,8 @@ $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) - devel-peer-request: unknown1 sending unknown1 command - devel-peer-request: unknown2 sending unknown2 command - devel-peer-request: unknown3 sending unknown3 command devel-peer-request: hello sending hello command diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -51,10 +51,6 @@ pass # Facilitates testing sshpeer without requiring an SSH server. -class testingsshpeer(sshpeer.sshpeer): -def _validaterepo(self, *args, **kwargs): -pass - class badpeer(httppeer.httppeer): def __init__(self): super(badpeer, self).__init__(uimod.ui(), 'http://localhost') @@ -69,8 +65,8 @@ checkobject(badpeer()) checkobject(httppeer.httppeer(ui, 'http://localhost')) checkobject(localrepo.localpeer(dummyrepo())) -checkobject(testingsshpeer(ui, 'ssh://localhost/foo', None, None, None, - None)) +checkobject(sshpeer.sshpeer(ui, 'ssh://localhost/foo', None, None, None, + None, None)) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/tests/sshprotoext.py b/tests/sshprotoext.py --- a/tests/sshprotoext.py +++ b/tests/sshprotoext.py @@ -12,6 +12,7 @@ from mercurial import ( error, +extensions, registrar, sshpeer, wireproto, @@ -52,30 +53,26 @@ super(prehelloserver, self).serve_forever() -class extrahandshakecommandspeer(sshpeer.sshpeer): -"""An ssh peer that sends extra commands as part of initial handshake.""" -def _validaterepo(self): -mode = self._ui.config(b'sshpeer', b'handshake-mode') -if mode == b'pre-no-args': -self._callstream(b'no-args') -return super(extrahandshakecommandspeer, self)._validaterepo() -elif mode == b'pre-multiple-no-args': -self._callstream(b'unknown1') -self._callstream(b'unknown2') -self._callstream(b'unknown3') -return super(extrahandshakecommandspeer, self)._validaterepo() -else: -raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' % - mode) - -def registercommands(): -def dummycommand(repo, proto): -raise error.ProgrammingError('this should never be called') - -wireproto.wireprotocommand(b'no-args', b'')(dummycommand) -wireproto.wireprotocommand(b'unknown1', b'')(dummycommand) -wireproto.wireprotocommand(b'unknown2', b'')(dummycommand) -wireproto.wireprotocommand(b'unknown3', b'')(dummycommand) +def performhandshake(orig, ui, stdin, stdout, stderr): +"""Wrapped version of sshpeer._performhandshake to send extra commands.""" +mode = ui.config(b'sshpeer', b'handshake-mode') +if mode == b'pre-no-args': +ui.debug(b'sending no-args command\n') +stdin.write(b'no-args\n') +stdin.flush() +return orig(ui, stdin, stdout, stderr) +elif mode == b'pre-multiple-no-args': +ui.debug(b'sending unknown1 command\n') +stdin.write(b'unknown1\n') +ui.debug(b'sending unknown2 command\n') +stdin.write(b'unknown2\n') +ui.debug(b'sending unknown3 command\n') +stdin.write(b'unknown3\n') +stdin.flush() +return orig(ui, stdin, stdout, stderr) +else: +raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' % + mode) def extsetup(ui): # It's easier for tests to define the server
D2035: sshpeer: document the handshake mechanism
This revision was automatically updated to reflect the committed changes. Closed by commit rHGa622a927fe03: sshpeer: document the handshake mechanism (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2035?vs=5236=5252 REVISION DETAIL https://phab.mercurial-scm.org/D2035 AFFECTED FILES mercurial/sshpeer.py CHANGE DETAILS diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -162,6 +162,37 @@ hint = ui.config('ui', 'ssherrorhint') raise error.RepoError(msg, hint=hint) +# The handshake consists of sending 2 wire protocol commands: +# ``hello`` and ``between``. +# +# The ``hello`` command (which was introduced in Mercurial 0.9.1) +# instructs the server to advertise its capabilities. +# +# The ``between`` command (which has existed in all Mercurial servers +# for as long as SSH support has existed), asks for the set of revisions +# between a pair of revisions. +# +# The ``between`` command is issued with a request for the null +# range. If the remote is a Mercurial server, this request will +# generate a specific response: ``1\n\n``. This represents the +# wire protocol encoded value for ``\n``. We look for ``1\n\n`` +# in the output stream and know this is the response to ``between`` +# and we're at the end of our handshake reply. +# +# The response to the ``hello`` command will be a line with the +# length of the value returned by that command followed by that +# value. If the server doesn't support ``hello`` (which should be +# rare), that line will be ``0\n``. Otherwise, the value will contain +# RFC 822 like lines. Of these, the ``capabilities:`` line contains +# the capabilities of the server. +# +# In addition to the responses to our command requests, the server +# may emit "banner" output on stdout. SSH servers are allowed to +# print messages to stdout on login. Issuing commands on connection +# allows us to flush this banner output from the server by scanning +# for output to our well-known ``between`` command. Of course, if +# the banner contains ``1\n\n``, this will throw off our detection. + requestlog = ui.configbool('devel', 'debug.peer-request') try: @@ -205,6 +236,8 @@ caps = set() for l in reversed(lines): +# Look for response to ``hello`` command. Scan from the back so +# we don't misinterpret banner output as the command reply. if l.startswith('capabilities:'): caps.update(l[:-1].split(':')[1].split()) break To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2032: sshpeer: clean up API for sshpeer.__init__ (API)
This revision was automatically updated to reflect the committed changes. Closed by commit rHGf8f034344b39: sshpeer: clean up API for sshpeer.__init__ (API) (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2032?vs=5233=5249 REVISION DETAIL https://phab.mercurial-scm.org/D2032 AFFECTED FILES mercurial/sshpeer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -69,8 +69,8 @@ checkobject(badpeer()) checkobject(httppeer.httppeer(ui, 'http://localhost')) checkobject(localrepo.localpeer(dummyrepo())) -checkobject(testingsshpeer(ui, 'ssh://localhost/foo', False, - (None, None, None, None))) +checkobject(testingsshpeer(ui, 'ssh://localhost/foo', None, None, None, + None)) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -157,12 +157,21 @@ return proc, stdin, stdout, stderr class sshpeer(wireproto.wirepeer): -def __init__(self, ui, path, create=False, sshstate=None): -self._url = path +def __init__(self, ui, url, proc, stdin, stdout, stderr): +"""Create a peer from an existing SSH connection. + +``proc`` is a handle on the underlying SSH process. +``stdin``, ``stdout``, and ``stderr`` are handles on the stdio +pipes for that process. +""" +self._url = url self._ui = ui # self._subprocess is unused. Keeping a handle on the process # holds a reference and prevents it from being garbage collected. -self._subprocess, self._pipei, self._pipeo, self._pipee = sshstate +self._subprocess = proc +self._pipeo = stdin +self._pipei = stdout +self._pipee = stderr self._validaterepo() @@ -386,6 +395,4 @@ proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd, remotepath, sshenv) -sshstate = (proc, stdout, stdin, stderr) - -return sshpeer(ui, path, create=create, sshstate=sshstate) +return sshpeer(ui, path, proc, stdin, stdout, stderr) To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2033: sshpeer: inline I/O into _validaterepo()
This revision was automatically updated to reflect the committed changes. Closed by commit rHGa9cffd14aa04: sshpeer: inline I/O into _validaterepo() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2033?vs=5234=5250 REVISION DETAIL https://phab.mercurial-scm.org/D2033 AFFECTED FILES mercurial/sshpeer.py tests/sshprotoext.py CHANGE DETAILS diff --git a/tests/sshprotoext.py b/tests/sshprotoext.py --- a/tests/sshprotoext.py +++ b/tests/sshprotoext.py @@ -54,24 +54,16 @@ class extrahandshakecommandspeer(sshpeer.sshpeer): """An ssh peer that sends extra commands as part of initial handshake.""" -# There isn't a good hook point. So we wrap _callstream() and inject -# logic when the peer says "hello". -def _callstream(self, cmd, **args): -if cmd != b'hello': -return super(extrahandshakecommandspeer, self)._callstream(cmd, - **args) - +def _validaterepo(self): mode = self._ui.config(b'sshpeer', b'handshake-mode') if mode == b'pre-no-args': self._callstream(b'no-args') -return super(extrahandshakecommandspeer, self)._callstream( -cmd, **args) +return super(extrahandshakecommandspeer, self)._validaterepo() elif mode == b'pre-multiple-no-args': self._callstream(b'unknown1') self._callstream(b'unknown2') self._callstream(b'unknown3') -return super(extrahandshakecommandspeer, self)._callstream( -cmd, **args) +return super(extrahandshakecommandspeer, self)._validaterepo() else: raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' % mode) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -212,18 +212,37 @@ self._abort(error.RepoError(msg, hint=hint)) try: -# skip any noise generated by remote shell -self._callstream("hello") -r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40))) +pairsarg = '%s-%s' % ('0' * 40, '0' * 40) + +handshake = [ +'hello\n', +'between\n', +'pairs %d\n' % len(pairsarg), +pairsarg, +] + +requestlog = self.ui.configbool('devel', 'debug.peer-request') + +if requestlog: +self.ui.debug('devel-peer-request: hello\n') +self.ui.debug('sending hello command\n') +if requestlog: +self.ui.debug('devel-peer-request: between\n') +self.ui.debug('devel-peer-request: pairs: %d bytes\n' % + len(pairsarg)) +self.ui.debug('sending between command\n') + +self._pipeo.write(''.join(handshake)) +self._pipeo.flush() except IOError: badresponse() lines = ["", "dummy"] max_noise = 500 while lines[-1] and max_noise: try: -l = r.readline() -self._readerr() +l = self._pipei.readline() +_forwardoutput(self.ui, self._pipee) if lines[-1] == "1\n" and l == "\n": break if l: To: indygreg, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2027: sshpeer: move URL validation out of sshpeer.__init__
This revision was automatically updated to reflect the committed changes. Closed by commit rHGb202d360d2a4: sshpeer: move URL validation out of sshpeer.__init__ (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2027?vs=5185=5244 REVISION DETAIL https://phab.mercurial-scm.org/D2027 AFFECTED FILES mercurial/sshpeer.py CHANGE DETAILS diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -121,13 +121,6 @@ self._pipeo = self._pipei = self._pipee = None u = util.url(path, parsequery=False, parsefragment=False) -if u.scheme != 'ssh' or not u.host or u.path is None: -self._abort(error.RepoError(_("couldn't parse location %s") % path)) - -util.checksafessh(path) - -if u.passwd is not None: -self._abort(error.RepoError(_("password in URL not supported"))) self._user = u.user self._host = u.host @@ -371,4 +364,17 @@ self._readerr() def instance(ui, path, create): +"""Create an SSH peer. + +The returned object conforms to the ``wireproto.wirepeer`` interface. +""" +u = util.url(path, parsequery=False, parsefragment=False) +if u.scheme != 'ssh' or not u.host or u.path is None: +raise error.RepoError(_("couldn't parse location %s") % path) + +util.checksafessh(path) + +if u.passwd is not None: +raise error.RepoError(_('password in URL not supported')) + return sshpeer(ui, path, create=create) To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2031: sshpeer: establish SSH connection before class instantiation
This revision was automatically updated to reflect the committed changes. Closed by commit rHG00b9e26d727b: sshpeer: establish SSH connection before class instantiation (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2031?vs=5232=5248 REVISION DETAIL https://phab.mercurial-scm.org/D2031 AFFECTED FILES mercurial/sshpeer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -69,7 +69,8 @@ checkobject(badpeer()) checkobject(httppeer.httppeer(ui, 'http://localhost')) checkobject(localrepo.localpeer(dummyrepo())) -checkobject(testingsshpeer(ui, 'ssh://localhost/foo', False, ())) +checkobject(testingsshpeer(ui, 'ssh://localhost/foo', False, + (None, None, None, None))) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -131,16 +131,40 @@ pipee.close() +def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None): +"""Create an SSH connection to a server. + +Returns a tuple of (process, stdin, stdout, stderr) for the +spawned process. +""" +cmd = '%s %s %s' % ( +sshcmd, +args, +util.shellquote('%s -R %s serve --stdio' % ( +_serverquote(remotecmd), _serverquote(path + +ui.debug('running %s\n' % cmd) +cmd = util.quotecommand(cmd) + +# no buffer allow the use of 'select' +# feel free to remove buffering and select usage when we ultimately +# move to threading. +stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv) + +stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr) +stdin = doublepipe(ui, stdin, stderr) + +return proc, stdin, stdout, stderr + class sshpeer(wireproto.wirepeer): def __init__(self, ui, path, create=False, sshstate=None): self._url = path self._ui = ui -self._pipeo = self._pipei = self._pipee = None +# self._subprocess is unused. Keeping a handle on the process +# holds a reference and prevents it from being garbage collected. +self._subprocess, self._pipei, self._pipeo, self._pipee = sshstate -u = util.url(path, parsequery=False, parsefragment=False) -self._path = u.path or '.' - -self._validaterepo(*sshstate) +self._validaterepo() # Begin of _basepeer interface. @@ -172,28 +196,7 @@ # End of _basewirecommands interface. -def _validaterepo(self, sshcmd, args, remotecmd, sshenv=None): -assert self._pipei is None - -cmd = '%s %s %s' % (sshcmd, args, -util.shellquote("%s -R %s serve --stdio" % -(_serverquote(remotecmd), _serverquote(self._path -self.ui.debug('running %s\n' % cmd) -cmd = util.quotecommand(cmd) - -# while self._subprocess isn't used, having it allows the subprocess to -# to clean up correctly later -# -# no buffer allow the use of 'select' -# feel free to remove buffering and select usage when we ultimately -# move to threading. -sub = util.popen4(cmd, bufsize=0, env=sshenv) -self._pipeo, self._pipei, self._pipee, self._subprocess = sub - -self._pipei = util.bufferedinputpipe(self._pipei) -self._pipei = doublepipe(self.ui, self._pipei, self._pipee) -self._pipeo = doublepipe(self.ui, self._pipeo, self._pipee) - +def _validaterepo(self): def badresponse(): msg = _("no suitable response from remote hg") hint = self.ui.config("ui", "ssherrorhint") @@ -380,6 +383,9 @@ if res != 0: raise error.RepoError(_('could not create remote repo')) -sshstate = (sshcmd, args, remotecmd, sshenv) +proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd, + remotepath, sshenv) + +sshstate = (proc, stdout, stdin, stderr) return sshpeer(ui, path, create=create, sshstate=sshstate) To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2030: sshpeer: remove frivolous call to _cleanup()
This revision was automatically updated to reflect the committed changes. Closed by commit rHG94ba29934f00: sshpeer: remove frivolous call to _cleanup() (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2030?vs=5188=5247 REVISION DETAIL https://phab.mercurial-scm.org/D2030 AFFECTED FILES mercurial/sshpeer.py CHANGE DETAILS diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -173,8 +173,7 @@ # End of _basewirecommands interface. def _validaterepo(self, sshcmd, args, remotecmd, sshenv=None): -# cleanup up previous run -self._cleanup() +assert self._pipei is None cmd = '%s %s %s' % (sshcmd, args, util.shellquote("%s -R %s serve --stdio" % To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2029: sshpeer: extract pipe cleanup logic to own function
This revision was automatically updated to reflect the committed changes. Closed by commit rHG805edf16e8e0: sshpeer: extract pipe cleanup logic to own function (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2029?vs=5187=5246 REVISION DETAIL https://phab.mercurial-scm.org/D2029 AFFECTED FILES mercurial/sshpeer.py CHANGE DETAILS diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -114,6 +114,23 @@ def flush(self): return self._main.flush() +def _cleanuppipes(ui, pipei, pipeo, pipee): +"""Clean up pipes used by an SSH connection.""" +if pipeo: +pipeo.close() +if pipei: +pipei.close() + +if pipee: +# Try to read from the err descriptor until EOF. +try: +for l in pipee: +ui.status(_('remote: '), l) +except (IOError, ValueError): +pass + +pipee.close() + class sshpeer(wireproto.wirepeer): def __init__(self, ui, path, create=False, sshstate=None): self._url = path @@ -221,17 +238,7 @@ raise exception def _cleanup(self): -if self._pipeo is None: -return -self._pipeo.close() -self._pipei.close() -try: -# read the error descriptor until EOF -for l in self._pipee: -self.ui.status(_("remote: "), l) -except (IOError, ValueError): -pass -self._pipee.close() +_cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee) __del__ = _cleanup To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2028: sshpeer: move ssh command and repo creation logic out of __init__
This revision was automatically updated to reflect the committed changes. Closed by commit rHG31449baf0936: sshpeer: move ssh command and repo creation logic out of __init__ (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2028?vs=5186=5245 REVISION DETAIL https://phab.mercurial-scm.org/D2028 AFFECTED FILES mercurial/sshpeer.py tests/test-check-interfaces.py CHANGE DETAILS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -69,7 +69,7 @@ checkobject(badpeer()) checkobject(httppeer.httppeer(ui, 'http://localhost')) checkobject(localrepo.localpeer(dummyrepo())) -checkobject(testingsshpeer(ui, 'ssh://localhost/foo')) +checkobject(testingsshpeer(ui, 'ssh://localhost/foo', False, ())) checkobject(bundlerepo.bundlepeer(dummyrepo())) checkobject(statichttprepo.statichttppeer(dummyrepo())) checkobject(unionrepo.unionpeer(dummyrepo())) diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -115,35 +115,15 @@ return self._main.flush() class sshpeer(wireproto.wirepeer): -def __init__(self, ui, path, create=False): +def __init__(self, ui, path, create=False, sshstate=None): self._url = path self._ui = ui self._pipeo = self._pipei = self._pipee = None u = util.url(path, parsequery=False, parsefragment=False) - -self._user = u.user -self._host = u.host -self._port = u.port self._path = u.path or '.' -sshcmd = self.ui.config("ui", "ssh") -remotecmd = self.ui.config("ui", "remotecmd") -sshaddenv = dict(self.ui.configitems("sshenv")) -sshenv = util.shellenviron(sshaddenv) - -args = util.sshargs(sshcmd, self._host, self._user, self._port) - -if create: -cmd = '%s %s %s' % (sshcmd, args, -util.shellquote("%s init %s" % -(_serverquote(remotecmd), _serverquote(self._path -ui.debug('running %s\n' % cmd) -res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv) -if res != 0: -self._abort(error.RepoError(_("could not create remote repo"))) - -self._validaterepo(sshcmd, args, remotecmd, sshenv) +self._validaterepo(*sshstate) # Begin of _basepeer interface. @@ -377,4 +357,23 @@ if u.passwd is not None: raise error.RepoError(_('password in URL not supported')) -return sshpeer(ui, path, create=create) +sshcmd = ui.config('ui', 'ssh') +remotecmd = ui.config('ui', 'remotecmd') +sshaddenv = dict(ui.configitems('sshenv')) +sshenv = util.shellenviron(sshaddenv) +remotepath = u.path or '.' + +args = util.sshargs(sshcmd, u.host, u.user, u.port) + +if create: +cmd = '%s %s %s' % (sshcmd, args, +util.shellquote('%s init %s' % +(_serverquote(remotecmd), _serverquote(remotepath +ui.debug('running %s\n' % cmd) +res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv) +if res != 0: +raise error.RepoError(_('could not create remote repo')) + +sshstate = (sshcmd, args, remotecmd, sshenv) + +return sshpeer(ui, path, create=create, sshstate=sshstate) To: indygreg, #hg-reviewers, lothiraldan, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2026: tests: add low-level SSH protocol tests
This revision was automatically updated to reflect the committed changes. Closed by commit rHG83d67257ba90: tests: add low-level SSH protocol tests (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2026?vs=5227=5243 REVISION DETAIL https://phab.mercurial-scm.org/D2026 AFFECTED FILES tests/sshprotoext.py tests/test-ssh-proto.t CHANGE DETAILS diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t new file mode 100644 --- /dev/null +++ b/tests/test-ssh-proto.t @@ -0,0 +1,398 @@ + $ cat >> $HGRCPATH << EOF + > [ui] + > ssh = $PYTHON "$TESTDIR/dummyssh" + > [devel] + > debug.peer-request = true + > [extensions] + > sshprotoext = $TESTDIR/sshprotoext.py + > EOF + + $ hg init server + $ cd server + $ echo 0 > foo + $ hg -q add foo + $ hg commit -m initial + $ cd .. + +Test a normal behaving server, for sanity + + $ hg --debug debugpeer ssh://user@dummy/server + running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) + devel-peer-request: hello + sending hello command + devel-peer-request: between + devel-peer-request: pairs: 81 bytes + sending between command + remote: 384 + remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + remote: 1 + url: ssh://user@dummy/server + local: no + pushable: yes + +Server should answer the "hello" command in isolation + + $ hg -R server serve --stdio << EOF + > hello + > EOF + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + +>=0.9.1 clients send a "hello" + "between" for the null range as part of handshake. +Server should reply with capabilities and should send "1\n\n" as a successful +reply with empty response to the "between". + + $ hg -R server serve --stdio << EOF + > hello + > between + > pairs 81 + > - + > EOF + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + 1 + + +SSH banner is not printed by default, ignored by clients + + $ SSHSERVERMODE=banner hg debugpeer ssh://user@dummy/server + url: ssh://user@dummy/server + local: no + pushable: yes + +--debug will print the banner + + $ SSHSERVERMODE=banner hg --debug debugpeer ssh://user@dummy/server + running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) + devel-peer-request: hello + sending hello command + devel-peer-request: between + devel-peer-request: pairs: 81 bytes + sending between command + remote: banner: line 0 + remote: banner: line 1 + remote: banner: line 2 + remote: banner: line 3 + remote: banner: line 4 + remote: banner: line 5 + remote: banner: line 6 + remote: banner: line 7 + remote: banner: line 8 + remote: banner: line 9 + remote: 384 + remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + remote: 1 + url: ssh://user@dummy/server + local: no + pushable: yes + +And test the banner with the raw protocol + + $ SSHSERVERMODE=banner hg -R server serve --stdio << EOF + > hello + > between + > pairs 81 + > - + > EOF + banner: line 0 + banner: line 1 + banner: line 2 + banner: line 3 + banner: line 4 + banner: line 5 + banner: line 6 + banner: line 7 + banner: line 8 + banner: line 9 + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + 1 + + +Connecting to a <0.9.1 server that doesn't support the hello command + + $ SSHSERVERMODE=no-hello hg --debug debugpeer ssh://user@dummy/server + running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) + devel-peer-request: hello + sending hello command + devel-peer-request: between + devel-peer-request: pairs: 81 bytes + sending between command + remote: 0 + remote: 1 + url: ssh://user@dummy/server + local: no + pushable: yes + +The client should interpret this as no capabilities + + $ SSHSERVERMODE=no-hello hg debugcapabilities ssh://user@dummy/server + Main capabilities: + +Sending an unknown command to the server results in an empty response to that command + + $ hg -R server serve --stdio << EOF + > pre-hello + > hello + > between + > pairs 81 + > - + >
D2024: sshpeer: make "instance" a function
This revision was automatically updated to reflect the committed changes. Closed by commit rHGb0d2885c5945: sshpeer: make instance a function (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2024?vs=5182=5241 REVISION DETAIL https://phab.mercurial-scm.org/D2024 AFFECTED FILES mercurial/sshpeer.py CHANGE DETAILS diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -370,4 +370,5 @@ self._pipeo.flush() self._readerr() -instance = sshpeer +def instance(ui, path, create): +return sshpeer(ui, path, create=create) To: indygreg, #hg-reviewers, yuja Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 09 of 13] revlog: use context manager for data file lifetime in checkinlinesize
# HG changeset patch # User Boris Feld# Date 1517848487 -3600 # Mon Feb 05 17:34:47 2018 +0100 # Node ID c4015bae897ca99b12933bf74a176fa6a349681b # Parent da0b895c385747d820294509d102d633419f8036 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r c4015bae897c revlog: use context manager for data file lifetime in checkinlinesize This is clearer, safer and more modern. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1859,12 +1859,9 @@ class revlog(object): fp.flush() fp.close() -df = self._datafp('w') -try: +with self._datafp('w') as df: for r in self: df.write(self._getsegmentforrevs(r, r)[1]) -finally: -df.close() fp = self._indexfp('w') self.version &= ~FLAG_INLINE_DATA ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 10 of 13] revlog: use context manager for index file lifetime in checkinlinesize
# HG changeset patch # User Boris Feld# Date 1517848497 -3600 # Mon Feb 05 17:34:57 2018 +0100 # Node ID 1a212682563740e031380befc949167e73b476bd # Parent c4015bae897ca99b12933bf74a176fa6a349681b # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 1a2126825637 revlog: use context manager for index file lifetime in checkinlinesize This is clearer, safer and more modern. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1863,16 +1863,16 @@ class revlog(object): for r in self: df.write(self._getsegmentforrevs(r, r)[1]) -fp = self._indexfp('w') -self.version &= ~FLAG_INLINE_DATA -self._inline = False -for i in self: -e = self._io.packentry(self.index[i], self.node, self.version, i) -fp.write(e) +with self._indexfp('w') as fp: +self.version &= ~FLAG_INLINE_DATA +self._inline = False +io = self._io +for i in self: +e = io.packentry(self.index[i], self.node, self.version, i) +fp.write(e) -# if we don't call close, the temp file will never replace the -# real index -fp.close() +# the temp file replace the real index when we exit the context +# manager tr.replace(self.indexfile, trindex * self._io.size) self._chunkclear() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 07 of 13] revlog: move index file opening in a method
# HG changeset patch # User Boris Feld# Date 1517847733 -3600 # Mon Feb 05 17:22:13 2018 +0100 # Node ID d238ec45ba25f7c1ea9ec22aa11a4ec699c72740 # Parent b192ee27b376523292ec1cf52c49c9382a870658 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r d238ec45ba25 revlog: move index file opening in a method Having file operation centralized into a single spot help to factor common logic out (eg: special flag handling according to the mode). It is also the first step to simplify file handling during batch operation (eg: revlog cloning). However, that part does not seems to be a hotspot yet. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -621,7 +621,7 @@ class revlog(object): indexdata = '' self._initempty = True try: -f = self.opener(self.indexfile) +f = self._indexfp() if (mmapindexthreshold is not None and self.opener.fstat(f).st_size >= mmapindexthreshold): indexdata = util.buffer(util.mmapread(f)) @@ -682,6 +682,15 @@ class revlog(object): def _compressor(self): return util.compengines[self._compengine].revlogcompressor() +def _indexfp(self, mode='r'): +"""file object for the revlog's index file""" +args = {'mode': mode} +if mode != 'r': +args['checkambig'] = self._checkambig +if mode == 'w': +args['atomictemp'] = True +return self.opener(self.indexfile, **args) + def _datafp(self, mode='r'): """file object for the revlog's data file""" return self.opener(self.datafile, mode=mode) @@ -1498,7 +1507,7 @@ class revlog(object): closehandle = False else: if self._inline: -df = self.opener(self.indexfile) +df = self._indexfp() else: df = self._datafp() closehandle = True @@ -1858,8 +1867,7 @@ class revlog(object): finally: df.close() -fp = self.opener(self.indexfile, 'w', atomictemp=True, - checkambig=self._checkambig) +fp = self._indexfp('w') self.version &= ~FLAG_INLINE_DATA self._inline = False for i in self: @@ -1928,7 +1936,7 @@ class revlog(object): dfh = None if not self._inline: dfh = self._datafp("a+") -ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) +ifh = self._indexfp("a+") try: return self._addrevision(node, rawtext, transaction, link, p1, p2, flags, cachedelta, ifh, dfh, @@ -2157,7 +2165,7 @@ class revlog(object): end = 0 if r: end = self.end(r - 1) -ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) +ifh = self._indexfp("a+") isize = r * self._io.size if self._inline: transaction.add(self.indexfile, end + isize, r) @@ -2229,8 +2237,7 @@ class revlog(object): # reopen the index ifh.close() dfh = self._datafp("a+") -ifh = self.opener(self.indexfile, "a+", - checkambig=self._checkambig) +ifh = self._indexfp("a+") finally: if dfh: dfh.close() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 13 of 13] revlog: rename 'self.checkinlinesize' into '_enforceinlinesize'
# HG changeset patch # User Boris Feld# Date 1517850280 -3600 # Mon Feb 05 18:04:40 2018 +0100 # Node ID 70bf9e3148bf504164f1e5c3a40c92ced158e921 # Parent 6e3ed0354bf4f160bb7e1402ca1548282ba8b772 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 70bf9e3148bf revlog: rename 'self.checkinlinesize' into '_enforceinlinesize' The name change has two motivations: 1) The function has no external caller, so we move it to protected space. 2) the function does more than checking it also split the data if we have more data than 'inline' supports. diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -432,7 +432,7 @@ class changelog(revlog.revlog): self._delaybuf = None self._divert = False # split when we're done -self.checkinlinesize(tr) +self._enforceinlinesize(tr) def _writepending(self, tr): "create a file containing the unfinalized state for pretxnchangegroup" @@ -458,9 +458,9 @@ class changelog(revlog.revlog): return False -def checkinlinesize(self, tr, fp=None): +def _enforceinlinesize(self, tr, fp=None): if not self._delayed: -revlog.revlog.checkinlinesize(self, tr, fp) +revlog.revlog._enforceinlinesize(self, tr, fp) def read(self, node): """Obtain data from a parsed changelog revision. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1834,7 +1834,7 @@ class revlog(object): raise RevlogError(_("integrity check failed on %s:%s") % (self.indexfile, pycompat.bytestr(revornode))) -def checkinlinesize(self, tr, fp=None): +def _enforceinlinesize(self, tr, fp=None): """Check if the revlog is too big for inline and convert if so. This should be called after revisions are added to the revlog. If the @@ -2145,7 +2145,7 @@ class revlog(object): ifh.write(entry) ifh.write(data[0]) ifh.write(data[1]) -self.checkinlinesize(transaction, ifh) +self._enforceinlinesize(transaction, ifh) def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None): """ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 12 of 13] revlog: add a _datareadfp context manager for data access needs
# HG changeset patch # User Boris Feld# Date 1517849849 -3600 # Mon Feb 05 17:57:29 2018 +0100 # Node ID 6e3ed0354bf4f160bb7e1402ca1548282ba8b772 # Parent 9073bbc68cb1ccc5685c78dbe837f31406324028 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 6e3ed0354bf4 revlog: add a _datareadfp context manager for data access needs The helper handles: 1) is there a file handle already open that we shall just reuse, 2) is the revlog inlined or not. Using a context manager for all read access will help setting up file pointer caching in later changesets. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import binascii import collections +import contextlib import errno import hashlib import heapq @@ -694,6 +695,19 @@ class revlog(object): """file object for the revlog's data file""" return self.opener(self.datafile, mode=mode) +@contextlib.contextmanager +def _datareadfp(self, existingfp=None): +"""file object suitable to read data""" +if existingfp is not None: +yield existingfp +else: +if self._inline: +func = self._indexfp +else: +func = self._datafp +with func() as fp: +yield fp + def tip(self): return self.node(len(self.index) - 2) def __contains__(self, rev): @@ -1502,15 +1516,6 @@ class revlog(object): Returns a str or buffer of raw byte data. """ -if df is not None: -closehandle = False -else: -if self._inline: -df = self._indexfp() -else: -df = self._datafp() -closehandle = True - # Cache data both forward and backward around the requested # data, in a fixed size window. This helps speed up operations # involving reading the revlog backwards. @@ -1518,10 +1523,9 @@ class revlog(object): realoffset = offset & ~(cachesize - 1) reallength = (((offset + length + cachesize) & ~(cachesize - 1)) - realoffset) -df.seek(realoffset) -d = df.read(reallength) -if closehandle: -df.close() +with self._datareadfp(df) as df: +df.seek(realoffset) +d = df.read(reallength) self._cachesegment(realoffset, d) if offset != realoffset or reallength != length: return util.buffer(d, offset - realoffset, length) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 08 of 13] revlog: use context manager for index file life time in __init__
# HG changeset patch # User Boris Feld# Date 1517848459 -3600 # Mon Feb 05 17:34:19 2018 +0100 # Node ID da0b895c385747d820294509d102d633419f8036 # Parent d238ec45ba25f7c1ea9ec22aa11a4ec699c72740 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r da0b895c3857 revlog: use context manager for index file life time in __init__ This is clearer, safer and more modern. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -621,13 +621,12 @@ class revlog(object): indexdata = '' self._initempty = True try: -f = self._indexfp() -if (mmapindexthreshold is not None and -self.opener.fstat(f).st_size >= mmapindexthreshold): -indexdata = util.buffer(util.mmapread(f)) -else: -indexdata = f.read() -f.close() +with self._indexfp() as f: +if (mmapindexthreshold is not None and +self.opener.fstat(f).st_size >= mmapindexthreshold): +indexdata = util.buffer(util.mmapread(f)) +else: +indexdata = f.read() if len(indexdata) > 0: v = versionformat_unpack(indexdata[:4])[0] self._initempty = False ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 11 of 13] revlog: use context manager for data file lifetime in checksize
# HG changeset patch # User Boris Feld# Date 1517848514 -3600 # Mon Feb 05 17:35:14 2018 +0100 # Node ID 9073bbc68cb1ccc5685c78dbe837f31406324028 # Parent 1a212682563740e031380befc949167e73b476bd # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 9073bbc68cb1 revlog: use context manager for data file lifetime in checksize This is clearer, safer and more modern. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2335,10 +2335,9 @@ class revlog(object): expected = max(0, self.end(len(self) - 1)) try: -f = self._datafp() -f.seek(0, 2) -actual = f.tell() -f.close() +with self._datafp() as f: +f.seek(0, 2) +actual = f.tell() dd = actual - expected except IOError as inst: if inst.errno != errno.ENOENT: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 06 of 13] revlog: move datafile opening in a method
# HG changeset patch # User Boris Feld# Date 1517846636 -3600 # Mon Feb 05 17:03:56 2018 +0100 # Node ID b192ee27b376523292ec1cf52c49c9382a870658 # Parent dddbb1b848527a89ca887fdbec25725241efa376 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r b192ee27b376 revlog: move datafile opening in a method Having file operation centralized into a single spot help to factor common logic out. It is also the first step to simplify file handling during batch operation (eg: revlog cloning). However, that part does not seems to be a hotspot yet. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -682,6 +682,10 @@ class revlog(object): def _compressor(self): return util.compengines[self._compengine].revlogcompressor() +def _datafp(self, mode='r'): +"""file object for the revlog's data file""" +return self.opener(self.datafile, mode=mode) + def tip(self): return self.node(len(self.index) - 2) def __contains__(self, rev): @@ -1496,7 +1500,7 @@ class revlog(object): if self._inline: df = self.opener(self.indexfile) else: -df = self.opener(self.datafile) +df = self._datafp() closehandle = True # Cache data both forward and backward around the requested @@ -1847,7 +1851,7 @@ class revlog(object): fp.flush() fp.close() -df = self.opener(self.datafile, 'w') +df = self._datafp('w') try: for r in self: df.write(self._getsegmentforrevs(r, r)[1]) @@ -1923,7 +1927,7 @@ class revlog(object): """ dfh = None if not self._inline: -dfh = self.opener(self.datafile, "a+") +dfh = self._datafp("a+") ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) try: return self._addrevision(node, rawtext, transaction, link, p1, p2, @@ -2161,7 +2165,7 @@ class revlog(object): else: transaction.add(self.indexfile, isize, r) transaction.add(self.datafile, end) -dfh = self.opener(self.datafile, "a+") +dfh = self._datafp("a+") def flush(): if dfh: dfh.flush() @@ -2224,7 +2228,7 @@ class revlog(object): # addrevision switched from inline to conventional # reopen the index ifh.close() -dfh = self.opener(self.datafile, "a+") +dfh = self._datafp("a+") ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) finally: @@ -2328,7 +2332,7 @@ class revlog(object): expected = max(0, self.end(len(self) - 1)) try: -f = self.opener(self.datafile) +f = self._datafp() f.seek(0, 2) actual = f.tell() f.close() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 05 of 13] parseindex: implement context manager method on the wrapper
# HG changeset patch # User Boris Feld# Date 1517916311 -3600 # Tue Feb 06 12:25:11 2018 +0100 # Node ID dddbb1b848527a89ca887fdbec25725241efa376 # Parent 53534aa0676becd566238ce945274853526e9823 # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r dddbb1b84852 parseindex: implement context manager method on the wrapper This is needed for incoming cleanups. diff --git a/tests/test-parseindex.t b/tests/test-parseindex.t --- a/tests/test-parseindex.t +++ b/tests/test-parseindex.t @@ -41,6 +41,13 @@ We approximate that by reducing the read > def __getattr__(self, key): > return getattr(self.real, key) > + > def __enter__(self): + > self.real.__enter__() + > return self + > + > def __exit__(self, *args, **kwargs): + > return self.real.__exit__(*args, **kwargs) + > > def opener(*args): > o = vfs.vfs(*args) > def wrapper(*a, **kwargs): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 02 of 13] changelog: add the missing 'closed' property on 'appender' object
# HG changeset patch # User Boris Feld# Date 1517911721 -3600 # Tue Feb 06 11:08:41 2018 +0100 # Node ID b9d09ad843263f81e9d561d76e65671af864cc6f # Parent cc2e588940df0435bf0e3013ebd45013a22fa8fe # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r b9d09ad84326 changelog: add the missing 'closed' property on 'appender' object This mimic file object further. diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -90,6 +90,11 @@ class appender(object): return self.offset def flush(self): pass + +@property +def closed(self): +return self.fp.closed + def close(self): self.fp.close() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 04 of 13] parseindex: also forward keyword argument in a debug wrapper
# HG changeset patch # User Boris Feld# Date 1517914357 -3600 # Tue Feb 06 11:52:37 2018 +0100 # Node ID 53534aa0676becd566238ce945274853526e9823 # Parent 4cd271a9bd82ddca149a0dd5a2ae819a4c1db8ef # EXP-Topic revlog-fp # Available At https://bitbucket.org/octobus/mercurial-devel/ # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 53534aa0676b parseindex: also forward keyword argument in a debug wrapper Otherwise, it gets in the way of a coming refactoring. diff --git a/tests/test-parseindex.t b/tests/test-parseindex.t --- a/tests/test-parseindex.t +++ b/tests/test-parseindex.t @@ -43,8 +43,8 @@ We approximate that by reducing the read > > def opener(*args): > o = vfs.vfs(*args) - > def wrapper(*a): - > f = o(*a) + > def wrapper(*a, **kwargs): + > f = o(*a, **kwargs) > return singlebyteread(f) > return wrapper > ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 7 V2] cmdutil: convert _revertprefetch() to a generic stored file hook (API)
> On Feb 6, 2018, at 7:20 AM, Yuya Nishiharawrote: > >> On Tue, 06 Feb 2018 00:29:06 -0500, Matt Harbison wrote: >> # HG changeset patch >> # User Matt Harbison >> # Date 1517771668 18000 >> # Sun Feb 04 14:14:28 2018 -0500 >> # Node ID ab23d9644edaf62b2c3927b735d2170fc76ca711 >> # Parent 94d427f881cfca5cae792c5eac4bf00e942106ec >> cmdutil: convert _revertprefetch() to a generic stored file hook (API) >> >> +"""Stub method for detecting extension wrapping of _revertprefetch(), to >> +issue a deprecation warning.""" >> + >> +_revertprefetch = _revertprefetchstub >> + >> +def _prefetchfiles(repo, ctx, files): >> +"""Let extensions changing the storage layer prefetch content for any >> non >> +merge based command.""" > > Random ideas: > > - prefetchfiles() could be hosted by mergemod so lfs won't need to wrap > applyupdates(). Would it be better to keep it in cmdutil, and call it just before calling applyupdates()? Calling mergemod.prefetchfiles() from archive, for example, seems weird, doesn’t it? Or am I misunderstanding? > - it could be a list of callback functions, instead of carefully wrapping > the function itself Just a raw list that everything can access, or should it have add/remove methods too? I’ve seen both patterns with hook registration, IIRC. > - should be a public function as archival.py depends on it for example? Good idea ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 7 V2] cmdutil: convert _revertprefetch() to a generic stored file hook (API)
On Tue, 06 Feb 2018 00:29:06 -0500, Matt Harbison wrote: > # HG changeset patch > # User Matt Harbison> # Date 1517771668 18000 > # Sun Feb 04 14:14:28 2018 -0500 > # Node ID ab23d9644edaf62b2c3927b735d2170fc76ca711 > # Parent 94d427f881cfca5cae792c5eac4bf00e942106ec > cmdutil: convert _revertprefetch() to a generic stored file hook (API) > --- a/mercurial/cmdutil.py > +++ b/mercurial/cmdutil.py > @@ -2862,7 +2862,14 @@ > > if not opts.get('dry_run'): > needdata = ('revert', 'add', 'undelete') > -_revertprefetch(repo, ctx, *[actions[name][0] for name in > needdata]) > +if _revertprefetch is not _revertprefetchstub: > +ui.deprecwarn("'cmdutil._revertprefetch' is deprecated, use " > + "'cmdutil._prefetchfiles'", '4.6', > stacklevel=1) > +_revertprefetch(repo, ctx, > +*[actions[name][0] for name in needdata]) > +oplist = [actions[name][0] for name in needdata] > +_prefetchfiles(repo, ctx, > + [f for sublist in oplist for f in sublist]) > _performrevert(repo, parents, ctx, actions, interactive, > tobackup) > > if targetsubs: > @@ -2875,8 +2882,15 @@ > raise error.Abort("subrepository '%s' does not exist in > %s!" >% (sub, short(ctx.node( > > -def _revertprefetch(repo, ctx, *files): > -"""Let extension changing the storage layer prefetch content""" > +def _revertprefetchstub(): Restored the original arguments in case it's called by wrapper. > +"""Stub method for detecting extension wrapping of _revertprefetch(), to > +issue a deprecation warning.""" > + > +_revertprefetch = _revertprefetchstub > + > +def _prefetchfiles(repo, ctx, files): > +"""Let extensions changing the storage layer prefetch content for any non > +merge based command.""" Random ideas: - prefetchfiles() could be hosted by mergemod so lfs won't need to wrap applyupdates(). - it could be a list of callback functions, instead of carefully wrapping the function itself - should be a public function as archival.py depends on it for example? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 1 of 7 V2] lfs: factor out a method for extracting the pointer of a single file
On Tue, 06 Feb 2018 00:29:04 -0500, Matt Harbison wrote: > # HG changeset patch > # User Matt Harbison> # Date 1517082796 18000 > # Sat Jan 27 14:53:16 2018 -0500 > # Node ID ae7b40a6cb0863e26e245966388d766adf065bac > # Parent 2912bed9b0c7d02a1cac6f56584c0c0a2f9debe4 > lfs: factor out a method for extracting the pointer of a single file Queued the series, thanks. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2057: translate base85.c into rust code
durin42 added a subscriber: indygreg. durin42 added a comment. I'd be curious to see what @indygreg has to say about this, maybe wait on his input before doing any work in response to my feedback? I do wonder if we should have at least three crates: 1. hgcli 2. libmercurial 3. hgcext The first one would be the command-line entry point, the last could use the cpython API, and libmercurial would be "pure rust" and open the door to eventually having a libhg or something that exports C functions and would be suitable for cffi and linking into other binaries? INLINE COMMENTS > base85.rs:22 > + > +pub fn b85encode(py: Python, text: , pad: i32) -> PyResult { > +let text = text.as_bytes(); I think I'd like to separate things a bit more and have a Python-free module, and then a glue module that we can use to call into the pure Rust. Part of the reason is that in my perfect world we won't use the cpython crate for speedups so they can be used from pypy as well. Separating them at least makes it easier to have an extern "C" version of the method that can be used from cffi instead of only through the CPython API. (Not sure what opinions others have. It's likely that I'll attempt this approach in the near future as part of a continued attempt to speed up `hg diff`.) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2057 To: Ivzhh, #hg-reviewers Cc: indygreg, durin42, kevincox, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D2059: hgsh: enable clang-format
durin42 created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Nothing looks awful, so we can just turn it on. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2059 AFFECTED FILES contrib/clang-format-blacklist contrib/hgsh/hgsh.c CHANGE DETAILS diff --git a/contrib/hgsh/hgsh.c b/contrib/hgsh/hgsh.c --- a/contrib/hgsh/hgsh.c +++ b/contrib/hgsh/hgsh.c @@ -48,65 +48,65 @@ * have such machine, set to NULL. */ #ifndef HG_GATEWAY -#define HG_GATEWAY "gateway" +#define HG_GATEWAY "gateway" #endif /* * HG_HOST: hostname of mercurial server. if any machine is allowed, set to * NULL. */ #ifndef HG_HOST -#define HG_HOST "mercurial" +#define HG_HOST "mercurial" #endif /* * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and * host username are same, set to NULL. */ #ifndef HG_USER -#define HG_USER "hg" +#define HG_USER "hg" #endif /* * HG_ROOT: root of tree full of mercurial repos. if you do not want to * validate location of repo when someone is try to access, set to NULL. */ #ifndef HG_ROOT -#define HG_ROOT "/home/hg/repos" +#define HG_ROOT "/home/hg/repos" #endif /* * HG: path to the mercurial executable to run. */ #ifndef HG -#define HG "/home/hg/bin/hg" +#define HG "/home/hg/bin/hg" #endif /* * HG_SHELL: shell to use for actions like "sudo" and "su" access to * mercurial user, and cron jobs. if you want to make these things * impossible, set to NULL. */ #ifndef HG_SHELL -#define HG_SHELLNULL +#define HG_SHELL NULL /* #define HG_SHELL"/bin/bash" */ #endif /* * HG_HELP: some way for users to get support if they have problem. if they * should not get helpful message, set to NULL. */ #ifndef HG_HELP -#define HG_HELP "please contact supp...@example.com for help." +#define HG_HELP "please contact supp...@example.com for help." #endif /* * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to * HG_HOST. if you want to use rsh instead (why?), you need to modify * arguments it is called with. see forward_through_gateway. */ #ifndef SSH -#define SSH "/usr/bin/ssh" +#define SSH "/usr/bin/ssh" #endif /* @@ -249,7 +249,6 @@ hg_serve, }; - /* * attempt to verify that a directory is really a hg repo, by testing * for the existence of a subdirectory. @@ -310,8 +309,7 @@ if (sscanf(argv[2], "hg init %as", ) == 1) { cmd = hg_init; - } - else if (sscanf(argv[2], "hg -R %as serve --stdio", ) == 1) { + } else if (sscanf(argv[2], "hg -R %as serve --stdio", ) == 1) { cmd = hg_serve; } else { goto badargs; diff --git a/contrib/clang-format-blacklist b/contrib/clang-format-blacklist --- a/contrib/clang-format-blacklist +++ b/contrib/clang-format-blacklist @@ -1,6 +1,5 @@ # Files that just need to be migrated to the formatter. # Do not add new files here! -contrib/hgsh/hgsh.c mercurial/cext/base85.c mercurial/cext/bdiff.c mercurial/cext/charencode.c 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
D2058: chg: enable clang-format on all .c and .h files
durin42 created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Nothing in here looks awful, so I think we may as well just do it. 1. skip-blame because it's just reformatting with no functionality change REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2058 AFFECTED FILES contrib/chg/chg.c contrib/chg/hgclient.c contrib/chg/hgclient.h contrib/chg/procutil.c contrib/chg/util.c contrib/chg/util.h contrib/clang-format-blacklist CHANGE DETAILS diff --git a/contrib/clang-format-blacklist b/contrib/clang-format-blacklist --- a/contrib/clang-format-blacklist +++ b/contrib/clang-format-blacklist @@ -1,12 +1,5 @@ # Files that just need to be migrated to the formatter. # Do not add new files here! -contrib/chg/chg.c -contrib/chg/hgclient.c -contrib/chg/hgclient.h -contrib/chg/procutil.c -contrib/chg/procutil.h -contrib/chg/util.c -contrib/chg/util.h contrib/hgsh/hgsh.c mercurial/cext/base85.c mercurial/cext/bdiff.c diff --git a/contrib/chg/util.h b/contrib/chg/util.h --- a/contrib/chg/util.h +++ b/contrib/chg/util.h @@ -32,4 +32,4 @@ int runshellcmd(const char *cmd, const char *envp[], const char *cwd); -#endif /* UTIL_H_ */ +#endif /* UTIL_H_ */ diff --git a/contrib/chg/util.c b/contrib/chg/util.c --- a/contrib/chg/util.c +++ b/contrib/chg/util.c @@ -62,7 +62,8 @@ static int debugmsgenabled = 0; static double debugstart = 0; -static double now() { +static double now() +{ struct timeval t; gettimeofday(, NULL); return t.tv_usec / 1e6 + t.tv_sec; diff --git a/contrib/chg/procutil.c b/contrib/chg/procutil.c --- a/contrib/chg/procutil.c +++ b/contrib/chg/procutil.c @@ -54,7 +54,7 @@ goto error; forwardsignal(sig); - if (raise(sig) < 0) /* resend to self */ + if (raise(sig) < 0) /* resend to self */ goto error; if (sigaction(sig, , ) < 0) goto error; @@ -205,8 +205,8 @@ close(pipefds[0]); close(pipefds[1]); - int r = execle("/bin/sh", "/bin/sh", "-c", pagercmd, NULL, - envp); + int r = + execle("/bin/sh", "/bin/sh", "-c", pagercmd, NULL, envp); if (r < 0) { abortmsgerrno("cannot start pager '%s'", pagercmd); } diff --git a/contrib/chg/hgclient.h b/contrib/chg/hgclient.h --- a/contrib/chg/hgclient.h +++ b/contrib/chg/hgclient.h @@ -22,9 +22,9 @@ pid_t hgc_peerpid(const hgclient_t *hgc); const char **hgc_validate(hgclient_t *hgc, const char *const args[], - size_t argsize); + size_t argsize); int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize); void hgc_attachio(hgclient_t *hgc); void hgc_setenv(hgclient_t *hgc, const char *const envp[]); -#endif /* HGCLIENT_H_ */ +#endif /* HGCLIENT_H_ */ diff --git a/contrib/chg/hgclient.c b/contrib/chg/hgclient.c --- a/contrib/chg/hgclient.c +++ b/contrib/chg/hgclient.c @@ -7,7 +7,7 @@ * GNU General Public License version 2 or any later version. */ -#include /* for ntohl(), htonl() */ +#include /* for ntohl(), htonl() */ #include #include #include @@ -26,33 +26,32 @@ #include "procutil.h" #include "util.h" -enum { - CAP_GETENCODING = 0x0001, - CAP_RUNCOMMAND = 0x0002, - /* cHg extension: */ - CAP_ATTACHIO = 0x0100, - CAP_CHDIR = 0x0200, - CAP_SETENV = 0x0800, - CAP_SETUMASK = 0x1000, - CAP_VALIDATE = 0x2000, - CAP_SETPROCNAME = 0x4000, +enum { CAP_GETENCODING = 0x0001, + CAP_RUNCOMMAND = 0x0002, + /* cHg extension: */ + CAP_ATTACHIO = 0x0100, + CAP_CHDIR = 0x0200, + CAP_SETENV = 0x0800, + CAP_SETUMASK = 0x1000, + CAP_VALIDATE = 0x2000, + CAP_SETPROCNAME = 0x4000, }; typedef struct { const char *name; unsigned int flag; } cappair_t; static const cappair_t captable[] = { - {"getencoding", CAP_GETENCODING}, - {"runcommand", CAP_RUNCOMMAND}, - {"attachio", CAP_ATTACHIO}, - {"chdir", CAP_CHDIR}, - {"setenv", CAP_SETENV}, - {"setumask", CAP_SETUMASK}, - {"validate", CAP_VALIDATE}, - {"setprocname", CAP_SETPROCNAME}, - {NULL, 0}, /* terminator */ +{"getencoding", CAP_GETENCODING}, +{"runcommand", CAP_RUNCOMMAND}, +{"attachio", CAP_ATTACHIO}, +{"chdir", CAP_CHDIR}, +{"setenv", CAP_SETENV}, +{"setumask", CAP_SETUMASK}, +{"validate", CAP_VALIDATE}, +{"setprocname", CAP_SETPROCNAME}, +{NULL, 0}, /* terminator */ }; typedef struct { @@ -88,8 +87,8 @@ if (newsize <= ctx->maxdatasize) return; - newsize = defaultdatasize - * ((newsize + defaultdatasize - 1) / defaultdatasize); + newsize = defaultdatasize * +