\x89\x50\x4E\x47\x0D\x0A\x1A\x0A PNG  \x89\x50\x4E\x47\x0D\x0A\x1A\x0A  13\c @@sdZddlmZddlZddlZddlZddlZddlmZm Z ddl m Z ddl m Z mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZddlm Z m!Z!dZ"iZ#ej$e#Z$iZ%ej&e%Z&e&d d d d e&d d d de&d dd de&d dd de&d dd de&ddd e(idd6dd6dd6dd6dd6dd6Z)ddde dfgZ*dZ+d Z,d!Z-d"Z.e+d#ge d$d%Z/d&Z0ej1d'Z2ej1d(ej3Z4d)Z5d*Z6d+Z7d,Z8dddddd-Z9d.Z:e+dd/d0ge d1e d2fdd3e;e d4fdd5ge d6fddde d7fge d8d9Z<ej=dxdydzd{gZ>dBZ?dCdDdEdFdGhZ@dHZAid dd|ddfdL6dMdd}ddfdO6dPddd~dfdR6dSddddfdU6dSddddfdW6ddK6ddX6ddY6ZBdZZCd[ZDd\ZEd]ZFd^ZGd_ZHd`ZIdaZJe+dbddce(e ddfge dedfZKe+dgddhe(e difddje(e dkfddle(e dmfddne(e dofdpdqde drfge dedsZLejMZMeMdtdudvhdwZNdS(ssimple Phabricator integration (EXPERIMENTAL) This extension provides a ``phabsend`` command which sends a stack of changesets to Phabricator, and a ``phabread`` command which prints a stack of revisions in a format suitable for :hg:`import`, and a ``phabupdate`` command to update statuses in batch. By default, Phabricator requires ``Test Plan`` which might prevent some changeset from being sent. The requirement could be disabled by changing ``differential.require-test-plan-field`` config server side. Config:: [phabricator] # Phabricator URL url = https://phab.example.com/ # Repo callsign. If a repo has a URL https://$HOST/diffusion/FOO, then its # callsign is "FOO". callsign = FOO # curl command to use. If not set (default), use builtin HTTP library to # communicate. If set, use the specified curl command. This could be useful # if you need to specify advanced options that is not easily supported by # the internal library. curlcmd = curl --connect-timeout 2 --retry 3 --silent [auth] example.schemes = https example.prefix = phab.example.com # API token. Get it from https://$HOST/conduit/login/ example.phabtoken = cli-xxxxxxxxxxxxxxxxxxxxxxxxxxxx i(tabsolute_importN(tbintnullid(t_(tcmdutiltcontexttencodingterrorthttpconnectiontmdifftobsutiltparsertpatcht registrartscmutiltsmartsetttagst templateutilturltutil(tprocutilt stringutilsships-with-hg-coret phabricatort batchsizetdefaulti tcallsigntcurlcmdtrepophidRtphabsendtconfirmtgreensphabricator.action.createdtmagentasphabricator.action.skippedsphabricator.action.updatedtsphabricator.desctboldsphabricator.drevsphabricator.nodestest-vcrsPath to a vcr file. If nonexistent, will record a new vcr transcript, otherwise will mock all http requests using the specified vcr file. (ADVANCED)c@s#|tfd}|S(Nc@s=fd}j|_j|_t|S(Nc @s|jdd}|rddl}|jyddl}ddlj}|jdddtd|j ftd|j fg}|j |||SWdQXWdQXn||S(Nttest_vcrit serializertjsontcustom_patchesRthttpsconnection( tpoptNonethgdemandimportt deactivatedtvcrt vcr.stubststubstVCRturlmodtVCRHTTPConnectiontVCRHTTPSConnectiont use_cassette(targstkwargstcassetteR)tvcrmodR-R+(tfn(s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytinners    (t__name__t__doc__tcommand(R7R8(t fullflagstnametspec(R7s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytdecorate~s  (t _VCR_FLAGS(R=tflagsR>R?((R<R=R>s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt vcrcommand|s c@s;tjfdd|tjjS(slike urlencode, but works with nested parameters. For example, if params is {'a': ['b', 'c'], 'd': {'e': 'f'}}, it will be flattened to {'a[0]': 'b', 'a[1]': 'c', 'd[e]': 'f'} and then passed to urlencode. Note: the encoding is consistent with PHP's http_build_query. c@st|tr*idt6dt6|}nitt6dt6jt|}|dkri||ss%s[%s]( t isinstancetbooltTruetFalset enumeratetlisttdicttgetttypeR((tprefixtobjREtktv(t flatparamstprocess(s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRVs&  R (Rtsortdictturlreqt urlencode(tparams((RURVs7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyturlencodenesteds  cC@s|jjdd}|s7tjtddntj|j|tj|j }d}|r|\}}|jj d||j d}n|stjtd|fn||fS( sreturn conduit url, token and make sure they exist Currently read from [auth] config section. In the future, it might make sense to read from .arcconfig and .arcrc as well. RRsconfig %s.%s is requireds#using auth.%s.* for authentication t phabtokens)Can't find conduit token associated to %s(RRN( tuitconfigRtAbortRthttpconnectionmodtreadauthforuriRRtuserR(tdebugRO(trepoRtresttokentgrouptauth((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt readurltokens $ cC@st|\}}tjdj|d|gj\}}|jjd||f|j}||dd St|dii|gd6d6}t|ddkr{d Stj|ddd }|jjdd||S( s.given callsign, return repository PHID or NoneRRRsdiffusion.repository.searcht callsignst constraintsRlitphidN(R]R^R(RtlenRt strtolocalt setconfig(RdRRtquery((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt getrepophids s\AD([1-9][0-9]*)\Zs>^Differential Revision:\s*(?P(?:.*)D(?P[1-9][0-9]*))$c @st|\}}|j}|jj}i}i}x|D]}||} ttj|j|g} xz| D]r} | |krrx]|j| D]I} t j | } | rdt | t | j df||9ssD%dtmessageRbtdatetlocals=D%s: local tag removed - does not match Differential history tkeycS@st|dS(NR(tint(R((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRGIsN(Rit unfilteredt changelogtnodemapRMR tallpredecessorstobsstoretnodetagst_differentialrevisiontagretmatchtsetRRgt_differentialrevisiondescretsearcht descriptiontvaluesRRERIRttagRR(RJR]twarnRtmax(RdtnodelistRRftunfiRRot toconfirmRtctxt precnodestnRtmtforcetprecstdrevtdrevstalldiffstnewnodetprecsetRtdiffst phprecsetttagnametoldnodetlastdiff((Rs7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytgetoldnodedrevmapsT      (/(  ""     cC@sjtj}xQtj|j|jj|jdd|D]\}}|j|qCW|j S(s:plain-text diff without header (user, commit message, etc)toptsN( RtstringioR tdiffuiRdtp1RR(Rutgetvalue(Rtdiffoptstoutputtchunkt_label((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytgetdiffRs  *cC@s|j}t|}it|tjdtddd6}|rS||d ! "      "   cC@sii|d6d6}t|d|}|d}td|D}t||}|rtjtddjt|ng|D]}|d^qS( sconvert user names to PHIDst usernamesRs user.searchRlcs@s|]}|ddVqdS(RtusernameN((Rtentry((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pys ssunknown username: %sRR(RRRR_RRptsorted(RdtnamesRRoRltresolvedt unresolvedR((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt userphidss trtrevsrevisions to sendtREVtamendsupdate commit messagestreviewersspecify reviewerss#ask for confirmation before sendings REV [OPTIONS]c*O@st||jdg}tj||}|sLtjtdn|jdrktj|nt |g|D]}||j ^qx}|j dd}|t |jdO}|rt |||}|stjtdqng}|jdg} | rFt|| } |jidd 6| d 6ng} i} d%} x|D] }|jd |||}|j|j d&\}}}||j ks|jdrt||| |||\}}|| |j int or None, ex. 'D45' -> 45; '12' -> 12; 'x' -> NonetDiN(t startswithtisdigitR(RF((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt _parsedrevs cC@st}t}|d}|dkrQt|d}|r|j|qn|dkrt|d\}}|j||j||j|nAx>|dD]2}t|\}}|j||j|qW||fS(s9return ({single-drev-id}, {ancestor-drev-id}) to prefetchiRFiR>(RRYRBt_prefetchdrevsR(RTRt ancestordrevstopRtatt((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRZs"        c@sfdfdit|}t|\}}jjdd}t|}x5|D]-}|jttd|||dqmW|rit|d6nt tt|t|Bfdg|D]}|^q S(sreturn a list of "Differential Revision" dicts spec is a string using a simple query language, see docstring in phabread for details. A "Differential Revision dict" looks like: { "id": "2", "phid": "PHID-DREV-672qvysjcczopag46qty", "title": "example", "uri": "https://phab.example.com/D2", "dateCreated": "1499181406", "dateModified": "1499182103", "authorPHID": "PHID-USER-tv3ohwc4v4jeu34otlye", "status": "0", "statusName": "Needs Review", "properties": [], "branch": null, "summary": "", "testPlan": "", "lineCount": "2", "activeDiffPHID": "PHID-DIFF-xoqnjkobbm6k4dk6hi72", "diffs": [ "3", "4", ], "commits": [], "reviewers": [], "ccs": [], "hashes": [], "auxiliary": { "phabricator:projects": [], "phabricator:depends-on": [ "PHID-DREV-gbapp366kutjebt7agcd" ] }, "repositoryPHID": "PHID-REPO-hub2hx62ieuqeheznasv", "sourcePath": null } c@s|jdp$|jdp$dgd}|kr?|Std|}x0|D](}||d<|t|d single drev or NoneRRisdifferential.queryRRs#cannot get Differential Revision %rN(ROR(RRRR_R(RZRRR(t prefetchedRd(s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytfetchs+    c @st}g}g|D]}i|gd6^q}x|r|j}|}|d|kriq5n|j|d|jt|d|jdi}|jdg}x%|D]} |ji| gd6qWq5W|jtj|S(s6given a top, get a stack from the bottom, [id] -> [id]RRt auxiliarysphabricator:depends-onR( RR'RBRRROtreverseRtbaseset( t topdrevidstvisitedRoR)tqueueRZRRatdependsR(R`(s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pytgetstacks  #     RRiRc@s<|d}|dkrt|d}|r<tj|gS|dtkrgD]&}t||dkrS|^qS}tj|Stjtd|dn|dddhkrtt ||d|dS|d kr|dS|d kr%|dStj d |dS( NiRFisunknown symbol: %sR@RBRDiRgR>sillegal tree: %r( RYRRct_knownstatusnamesR;RR_RtgetattrtoperatortProgrammingError(RTR\RRR(RhR_tvalididstwalk(s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRn!s"   &  *  ( RURZR]t configintRRtrangeRRMR(RdR>RTRR[RttofetchR((R`RhR_RdRmRns7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt querydrevs*   +(cC@sm|d}|dj}|dj}|r=d|}nd|d}djtd||||gS( sget description (commit message) from "Differential Revision" This is similar to differential.getcommitmessage API. But we only care about limited fields: title, summary, test plan, and URL. RRRs Test Plan: %ssDifferential Revision: %sturis N(trstripRptfilterR((RRRttestplanRs((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyR9s  cC@s|jdpi}|jd}| r|jdrt|djd}id|dd6|dd 6d |d |d fd 6}t|jdddkr|dd|d", "parent": "6d0abad76b30e4724a37ab8721d630394070fe16" } } Or converted from "local:commits", sent by "arc", like: "properties": { "local:commits": { "98c08acae292b2faf60a279b4189beb6cff1414d": { "author": "Foo Bar", "time": 1499546314, "branch": "default", "tag": "", "commit": "98c08acae292b2faf60a279b4189beb6cff1414d", "rev": "98c08acae292b2faf60a279b4189beb6cff1414d", "local": "1000", "parents": ["6d0abad76b30e4724a37ab8721d630394070fe16"], "summary": "...", "message": "...", "authorEmail": "foo@example.com" } } } Note: metadata extracted from "local:commits" will lose time zone information. t propertiesshg:metas local:commitsis%d 0RRRRs%s <%s>RRRbRiR((RORRR(RtpropstmetaR((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRGs% c C@s+ttd|D}t|di|d6}x|D]}|jjtd|dtd|dD}t|di|d 6}t|}d } t|t |} x?t j D]1} | | kr| d t | | | f7} qqWd | ||f} |t j | q<Wd S(sgenerate plain-text patch readable by 'hg import' write is usually ui.write. drevs is what "querydrev" returns, results of "differential.query". cs@s)|]}td|dDVqdS(cs@s|]}t|VqdS(N(R(RRT((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pys sRN(R(RR((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pys ssdifferential.querydiffsRs reading D%s Rcs@s|]}t|VqdS(N(R(RRT((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pys sRsdifferential.getrawdifftdiffIDs# HG changeset patch s# %s %s s%s%s %sN(RRRR]tnoteRRRRtstrt _metanamemaptkeysRR( RdRRutdiffidsRRtdiffidRRtheaderRyRStcontent((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt readpatchys    #tphabreadtstacksread dependenciessDREVSPEC [OPTIONS]cK@sB|jdrd|}nt||}t|||jdS(sPprint patches from Phabricator suitable for importing DREVSPEC could be a Differential Revision identity, like ``D123``, or just the number ``123``. It could also have common operators like ``+``, ``-``, ``&``, ``(``, ``)`` for complex queries. Prefix ``:`` could be used to select a stack. ``abandoned``, ``accepted``, ``closed``, ``needsreview``, ``needsrevision`` could be used to filter patches by status. For performance reason, they only represent a subset of non-status selections and cannot be used alone. For example, ``:D6+8-(2+D4)`` selects a stack up to D6, plus D8 and exclude D2 and D4. ``:D9 & needsreview`` selects "Needs Review" revisions in a stack up to D9. If --stack is given, follow dependencies information and read all patches. It is equivalent to the ``:`` operator. Rs:(%s)N(RORrRRu(R]RdR>RR((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRs t phabupdatetacceptsaccept revisionstrejectsreject revisionstabandonsabandon revisionstreclaimsreclaim revisionsRtcommentscomment on the last revisionc K@s?gdjD]}|j|r |^q }t|dkretjtddj|ng}x)|D]!}|ji|d6dd6qrWt||}xt |D]\} } | dt|kr|jdr|jidd6|dd6n|ri| d d 6|d 6} t |d | qqWd S(srupdate Differential Revision in batch DREVSPEC selects revisions. See :hg:`help phabread` for its usage. saccept reject abandon reclaimis%s cannot be used togethers, RPRCRRRRRsdifferential.revision.editN( RRORRR_RRpRRrRLR( R]RdR>RRRARtfRR)RRZ((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyRs .% %" t phabreviewtrequiresRcC@sg|j|d}tj|j}|rctji|jdd6dj|jdd6SdS(se:phabreview: Object describing the review for this changeset. Has attributes `url` and `id`. RRsD{}RN(tresourceRRRRt hybriddictRgtformat(RR(RR((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyttemplate_reviews  (RbR.(RR/(RsNode ID(RsParent (RgiR<(R>i(R@i(RBi(RDi(iNNNN(iRFNNN(iNNNN(OR:t __future__RRKR$Rktretmercurial.nodeRRtmercurial.i18nRt mercurialRRRRRR`R R R R R RRRRRR/Rtmercurial.utilsRRt testedwithtcmdtableR;t configtablet configitemR(RKt colortableR@RBR[RiRRRtcompileRtMRRRRRRRRJRRWR}R RiR;RQRPRURYRZRrRRRRRttemplatekeywordR(((s7/usr/lib64/python2.7/site-packages/hgext/phabricator.pyt(s    j             !  O   >            v  2