PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/lib64/python2.7/site-packages/mercurial/hgweb
Viewing File: /usr/lib64/python2.7/site-packages/mercurial/hgweb/webcommands.py
# # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import os, mimetypes, re, cgi, copy import webutil from mercurial import error, encoding, archival, templater, templatefilters from mercurial.node import short, hex, nullid from mercurial.util import binary from common import paritygen, staticfile, get_contact, ErrorResponse from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND from mercurial import graphmod, patch from mercurial import help as helpmod from mercurial import scmutil from mercurial.i18n import _ # __all__ is populated with the allowed commands. Be sure to add to it if # you're adding a new command, or the new command won't work. __all__ = [ 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev', 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff', 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help', ] def log(web, req, tmpl): if 'file' in req.form and req.form['file'][0]: return filelog(web, req, tmpl) else: return changelog(web, req, tmpl) def rawfile(web, req, tmpl): guessmime = web.configbool('web', 'guessmime', False) path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) if not path: content = manifest(web, req, tmpl) req.respond(HTTP_OK, web.ctype) return content try: fctx = webutil.filectx(web.repo, req) except error.LookupError, inst: try: content = manifest(web, req, tmpl) req.respond(HTTP_OK, web.ctype) return content except ErrorResponse: raise inst path = fctx.path() text = fctx.data() mt = 'application/binary' if guessmime: mt = mimetypes.guess_type(path)[0] if mt is None: mt = binary(text) and 'application/binary' or 'text/plain' if mt.startswith('text/'): mt += '; charset="%s"' % encoding.encoding req.respond(HTTP_OK, mt, path, body=text) return [] def _filerevision(web, tmpl, fctx): f = fctx.path() text = fctx.data() parity = paritygen(web.stripecount) if binary(text): mt = mimetypes.guess_type(f)[0] or 'application/octet-stream' text = '(binary:%s)' % mt def lines(): for lineno, t in enumerate(text.splitlines(True)): yield {"line": t, "lineid": "l%d" % (lineno + 1), "linenumber": "% 6d" % (lineno + 1), "parity": parity.next()} return tmpl("filerevision", file=f, path=webutil.up(f), text=lines(), rev=fctx.rev(), node=fctx.hex(), author=fctx.user(), date=fctx.date(), desc=fctx.description(), extra=fctx.extra(), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), child=webutil.children(fctx), rename=webutil.renamelink(fctx), permissions=fctx.manifest().flags(f)) def file(web, req, tmpl): path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) if not path: return manifest(web, req, tmpl) try: return _filerevision(web, tmpl, webutil.filectx(web.repo, req)) except error.LookupError, inst: try: return manifest(web, req, tmpl) except ErrorResponse: raise inst def _search(web, req, tmpl): query = req.form['rev'][0] revcount = web.maxchanges if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) lessvars['rev'] = query morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 morevars['rev'] = query def changelist(**map): count = 0 lower = encoding.lower qw = lower(query).split() def revgen(): cl = web.repo.changelog for i in xrange(len(web.repo) - 1, 0, -100): l = [] for j in cl.revs(max(0, i - 100), i + 1): ctx = web.repo[j] l.append(ctx) l.reverse() for e in l: yield e for ctx in revgen(): miss = 0 for q in qw: if not (q in lower(ctx.user()) or q in lower(ctx.description()) or q in lower(" ".join(ctx.files()))): miss = 1 break if miss: continue count += 1 n = ctx.node() showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) yield tmpl('searchentry', parity=parity.next(), author=ctx.user(), parent=webutil.parents(ctx), child=webutil.children(ctx), changelogtag=showtags, desc=ctx.description(), extra=ctx.extra(), date=ctx.date(), files=files, rev=ctx.rev(), node=hex(n), tags=webutil.nodetagsdict(web.repo, n), bookmarks=webutil.nodebookmarksdict(web.repo, n), inbranch=webutil.nodeinbranch(web.repo, ctx), branches=webutil.nodebranchdict(web.repo, ctx)) if count >= revcount: break tip = web.repo['tip'] parity = paritygen(web.stripecount) return tmpl('search', query=query, node=tip.hex(), entries=changelist, archives=web.archivelist("tip"), morevars=morevars, lessvars=lessvars) def changelog(web, req, tmpl, shortlog=False): if 'node' in req.form: ctx = webutil.changectx(web.repo, req) else: if 'rev' in req.form: hi = req.form['rev'][0] else: hi = 'tip' try: ctx = web.repo[hi] except error.RepoError: return _search(web, req, tmpl) # XXX redirect to 404 page? def changelist(latestonly, **map): l = [] # build a list in forward order for efficiency revs = [] if start < end: revs = web.repo.changelog.revs(start, end - 1) if latestonly: for r in revs: pass revs = (r,) for i in revs: ctx = web.repo[i] n = ctx.node() showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) l.append({"parity": parity.next(), "author": ctx.user(), "parent": webutil.parents(ctx, i - 1), "child": webutil.children(ctx, i + 1), "changelogtag": showtags, "desc": ctx.description(), "extra": ctx.extra(), "date": ctx.date(), "files": files, "rev": i, "node": hex(n), "tags": webutil.nodetagsdict(web.repo, n), "bookmarks": webutil.nodebookmarksdict(web.repo, n), "inbranch": webutil.nodeinbranch(web.repo, ctx), "branches": webutil.nodebranchdict(web.repo, ctx) }) for e in reversed(l): yield e revcount = shortlog and web.maxshortchanges or web.maxchanges if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 count = len(web.repo) pos = ctx.rev() start = max(0, pos - revcount + 1) end = min(count, start + revcount) pos = end - 1 parity = paritygen(web.stripecount, offset=start - end) changenav = webutil.revnav(web.repo).gen(pos, revcount, count) return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav, node=ctx.hex(), rev=pos, changesets=count, entries=lambda **x: changelist(latestonly=False, **x), latestentry=lambda **x: changelist(latestonly=True, **x), archives=web.archivelist("tip"), revcount=revcount, morevars=morevars, lessvars=lessvars) def shortlog(web, req, tmpl): return changelog(web, req, tmpl, shortlog = True) def changeset(web, req, tmpl): ctx = webutil.changectx(web.repo, req) basectx = webutil.basechangectx(web.repo, req) if basectx is None: basectx = ctx.p1() showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node()) showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark', ctx.node()) showbranch = webutil.nodebranchnodefault(ctx) files = [] parity = paritygen(web.stripecount) for blockno, f in enumerate(ctx.files()): template = f in ctx and 'filenodelink' or 'filenolink' files.append(tmpl(template, node=ctx.hex(), file=f, blockno=blockno + 1, parity=parity.next())) style = web.config('web', 'style', 'paper') if 'style' in req.form: style = req.form['style'][0] parity = paritygen(web.stripecount) diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style) parity = paritygen(web.stripecount) diffstatgen = webutil.diffstatgen(ctx, basectx) diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity) return tmpl('changeset', diff=diffs, rev=ctx.rev(), node=ctx.hex(), parent=webutil.parents(ctx), child=webutil.children(ctx), basenode=basectx.hex(), changesettag=showtags, changesetbookmark=showbookmarks, changesetbranch=showbranch, author=ctx.user(), desc=ctx.description(), extra=ctx.extra(), date=ctx.date(), files=files, diffsummary=lambda **x: webutil.diffsummary(diffstatgen), diffstat=diffstat, archives=web.archivelist(ctx.hex()), tags=webutil.nodetagsdict(web.repo, ctx.node()), bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()), branch=webutil.nodebranchnodefault(ctx), inbranch=webutil.nodeinbranch(web.repo, ctx), branches=webutil.nodebranchdict(web.repo, ctx)) rev = changeset def decodepath(path): """Hook for mapping a path in the repository to a path in the working copy. Extensions (e.g., largefiles) can override this to remap files in the virtual file system presented by the manifest command below.""" return path def manifest(web, req, tmpl): ctx = webutil.changectx(web.repo, req) path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) mf = ctx.manifest() node = ctx.node() files = {} dirs = {} parity = paritygen(web.stripecount) if path and path[-1] != "/": path += "/" l = len(path) abspath = "/" + path for full, n in mf.iteritems(): # the virtual path (working copy path) used for the full # (repository) path f = decodepath(full) if f[:l] != path: continue remain = f[l:] elements = remain.split('/') if len(elements) == 1: files[remain] = full else: h = dirs # need to retain ref to dirs (root) for elem in elements[0:-1]: if elem not in h: h[elem] = {} h = h[elem] if len(h) > 1: break h[None] = None # denotes files present if mf and not files and not dirs: raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path) def filelist(**map): for f in sorted(files): full = files[f] fctx = ctx.filectx(full) yield {"file": full, "parity": parity.next(), "basename": f, "date": fctx.date(), "size": fctx.size(), "permissions": mf.flags(full)} def dirlist(**map): for d in sorted(dirs): emptydirs = [] h = dirs[d] while isinstance(h, dict) and len(h) == 1: k, v = h.items()[0] if v: emptydirs.append(k) h = v path = "%s%s" % (abspath, d) yield {"parity": parity.next(), "path": path, "emptydirs": "/".join(emptydirs), "basename": d} return tmpl("manifest", rev=ctx.rev(), node=hex(node), path=abspath, up=webutil.up(abspath), upparity=parity.next(), fentries=filelist, dentries=dirlist, archives=web.archivelist(hex(node)), tags=webutil.nodetagsdict(web.repo, node), bookmarks=webutil.nodebookmarksdict(web.repo, node), inbranch=webutil.nodeinbranch(web.repo, ctx), branches=webutil.nodebranchdict(web.repo, ctx)) def tags(web, req, tmpl): i = list(reversed(web.repo.tagslist())) parity = paritygen(web.stripecount) def entries(notip, latestonly, **map): t = i if notip: t = [(k, n) for k, n in i if k != "tip"] if latestonly: t = t[:1] for k, n in t: yield {"parity": parity.next(), "tag": k, "date": web.repo[n].date(), "node": hex(n)} return tmpl("tags", node=hex(web.repo.changelog.tip()), entries=lambda **x: entries(False, False, **x), entriesnotip=lambda **x: entries(True, False, **x), latestentry=lambda **x: entries(True, True, **x)) def bookmarks(web, req, tmpl): i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] parity = paritygen(web.stripecount) def entries(latestonly, **map): if latestonly: t = [min(i)] else: t = sorted(i) for k, n in t: yield {"parity": parity.next(), "bookmark": k, "date": web.repo[n].date(), "node": hex(n)} return tmpl("bookmarks", node=hex(web.repo.changelog.tip()), entries=lambda **x: entries(latestonly=False, **x), latestentry=lambda **x: entries(latestonly=True, **x)) def branches(web, req, tmpl): tips = [] heads = web.repo.heads() parity = paritygen(web.stripecount) sortkey = lambda ctx: (not ctx.closesbranch(), ctx.rev()) def entries(limit, **map): count = 0 if not tips: for t, n in web.repo.branchtags().iteritems(): tips.append(web.repo[n]) for ctx in sorted(tips, key=sortkey, reverse=True): if limit > 0 and count >= limit: return count += 1 if not web.repo.branchheads(ctx.branch()): status = 'closed' elif ctx.node() not in heads: status = 'inactive' else: status = 'open' yield {'parity': parity.next(), 'branch': ctx.branch(), 'status': status, 'node': ctx.hex(), 'date': ctx.date()} return tmpl('branches', node=hex(web.repo.changelog.tip()), entries=lambda **x: entries(0, **x), latestentry=lambda **x: entries(1, **x)) def summary(web, req, tmpl): i = reversed(web.repo.tagslist()) def tagentries(**map): parity = paritygen(web.stripecount) count = 0 for k, n in i: if k == "tip": # skip tip continue count += 1 if count > 10: # limit to 10 tags break yield tmpl("tagentry", parity=parity.next(), tag=k, node=hex(n), date=web.repo[n].date()) def bookmarks(**map): parity = paritygen(web.stripecount) marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] for k, n in sorted(marks)[:10]: # limit to 10 bookmarks yield {'parity': parity.next(), 'bookmark': k, 'date': web.repo[n].date(), 'node': hex(n)} def branches(**map): parity = paritygen(web.stripecount) b = web.repo.branchtags() l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()] for r, n, t in sorted(l): yield {'parity': parity.next(), 'branch': t, 'node': hex(n), 'date': web.repo[n].date()} def changelist(**map): parity = paritygen(web.stripecount, offset=start - end) l = [] # build a list in forward order for efficiency revs = [] if start < end: revs = web.repo.changelog.revs(start, end - 1) for i in revs: ctx = web.repo[i] n = ctx.node() hn = hex(n) l.append(tmpl( 'shortlogentry', parity=parity.next(), author=ctx.user(), desc=ctx.description(), extra=ctx.extra(), date=ctx.date(), rev=i, node=hn, tags=webutil.nodetagsdict(web.repo, n), bookmarks=webutil.nodebookmarksdict(web.repo, n), inbranch=webutil.nodeinbranch(web.repo, ctx), branches=webutil.nodebranchdict(web.repo, ctx))) l.reverse() yield l tip = web.repo['tip'] count = len(web.repo) start = max(0, count - web.maxchanges) end = min(count, start + web.maxchanges) return tmpl("summary", desc=web.config("web", "description", "unknown"), owner=get_contact(web.config) or "unknown", lastchange=tip.date(), tags=tagentries, bookmarks=bookmarks, branches=branches, shortlog=changelist, node=tip.hex(), archives=web.archivelist("tip")) def filediff(web, req, tmpl): fctx, ctx = None, None try: fctx = webutil.filectx(web.repo, req) except LookupError: ctx = webutil.changectx(web.repo, req) path = webutil.cleanpath(web.repo, req.form['file'][0]) if path not in ctx.files(): raise if fctx is not None: n = fctx.node() path = fctx.path() ctx = fctx.changectx() else: n = ctx.node() # path already defined in except clause parity = paritygen(web.stripecount) style = web.config('web', 'style', 'paper') if 'style' in req.form: style = req.form['style'][0] diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style) rename = fctx and webutil.renamelink(fctx) or [] ctx = fctx and fctx or ctx return tmpl("filediff", file=path, node=hex(n), rev=ctx.rev(), date=ctx.date(), desc=ctx.description(), extra=ctx.extra(), author=ctx.user(), rename=rename, branch=webutil.nodebranchnodefault(ctx), parent=webutil.parents(ctx), child=webutil.children(ctx), diff=diffs) diff = filediff def comparison(web, req, tmpl): ctx = webutil.changectx(web.repo, req) if 'file' not in req.form: raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') path = webutil.cleanpath(web.repo, req.form['file'][0]) rename = path in ctx and webutil.renamelink(ctx[path]) or [] parsecontext = lambda v: v == 'full' and -1 or int(v) if 'context' in req.form: context = parsecontext(req.form['context'][0]) else: context = parsecontext(web.config('web', 'comparisoncontext', '5')) def filelines(f): if binary(f.data()): mt = mimetypes.guess_type(f.path())[0] if not mt: mt = 'application/octet-stream' return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))] return f.data().splitlines() if path in ctx: fctx = ctx[path] rightrev = fctx.filerev() rightnode = fctx.filenode() rightlines = filelines(fctx) parents = fctx.parents() if not parents: leftrev = -1 leftnode = nullid leftlines = () else: pfctx = parents[0] leftrev = pfctx.filerev() leftnode = pfctx.filenode() leftlines = filelines(pfctx) else: rightrev = -1 rightnode = nullid rightlines = () fctx = ctx.parents()[0][path] leftrev = fctx.filerev() leftnode = fctx.filenode() leftlines = filelines(fctx) comparison = webutil.compare(tmpl, context, leftlines, rightlines) return tmpl('filecomparison', file=path, node=hex(ctx.node()), rev=ctx.rev(), date=ctx.date(), desc=ctx.description(), extra=ctx.extra(), author=ctx.user(), rename=rename, branch=webutil.nodebranchnodefault(ctx), parent=webutil.parents(fctx), child=webutil.children(fctx), leftrev=leftrev, leftnode=hex(leftnode), rightrev=rightrev, rightnode=hex(rightnode), comparison=comparison) def annotate(web, req, tmpl): fctx = webutil.filectx(web.repo, req) f = fctx.path() parity = paritygen(web.stripecount) diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate') def annotate(**map): last = None if binary(fctx.data()): mt = (mimetypes.guess_type(fctx.path())[0] or 'application/octet-stream') lines = enumerate([((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]) else: lines = enumerate(fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)) for lineno, ((f, targetline), l) in lines: fnode = f.filenode() if last != fnode: last = fnode yield {"parity": parity.next(), "node": f.hex(), "rev": f.rev(), "author": f.user(), "desc": f.description(), "extra": f.extra(), "file": f.path(), "targetline": targetline, "line": l, "lineid": "l%d" % (lineno + 1), "linenumber": "% 6d" % (lineno + 1), "revdate": f.date()} return tmpl("fileannotate", file=f, annotate=annotate, path=webutil.up(f), rev=fctx.rev(), node=fctx.hex(), author=fctx.user(), date=fctx.date(), desc=fctx.description(), extra=fctx.extra(), rename=webutil.renamelink(fctx), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), child=webutil.children(fctx), permissions=fctx.manifest().flags(f)) def filelog(web, req, tmpl): try: fctx = webutil.filectx(web.repo, req) f = fctx.path() fl = fctx.filelog() except error.LookupError: f = webutil.cleanpath(web.repo, req.form['file'][0]) fl = web.repo.file(f) numrevs = len(fl) if not numrevs: # file doesn't exist at all raise rev = webutil.changectx(web.repo, req).rev() first = fl.linkrev(0) if rev < first: # current rev is from before file existed raise frev = numrevs - 1 while fl.linkrev(frev) > rev: frev -= 1 fctx = web.repo.filectx(f, fl.linkrev(frev)) revcount = web.maxshortchanges if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 count = fctx.filerev() + 1 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page end = min(count, start + revcount) # last rev on this page parity = paritygen(web.stripecount, offset=start - end) def entries(latestonly, **map): l = [] repo = web.repo revs = repo.changelog.revs(start, end - 1) if latestonly: for r in revs: pass revs = (r,) for i in revs: iterfctx = fctx.filectx(i) l.append({"parity": parity.next(), "filerev": i, "file": f, "node": iterfctx.hex(), "author": iterfctx.user(), "date": iterfctx.date(), "rename": webutil.renamelink(iterfctx), "parent": webutil.parents(iterfctx), "child": webutil.children(iterfctx), "desc": iterfctx.description(), "extra": iterfctx.extra(), "tags": webutil.nodetagsdict(repo, iterfctx.node()), "bookmarks": webutil.nodebookmarksdict( repo, iterfctx.node()), "branch": webutil.nodebranchnodefault(iterfctx), "inbranch": webutil.nodeinbranch(repo, iterfctx), "branches": webutil.nodebranchdict(repo, iterfctx)}) for e in reversed(l): yield e revnav = webutil.filerevnav(web.repo, fctx.path()) nav = revnav.gen(end - 1, revcount, count) return tmpl("filelog", file=f, node=fctx.hex(), nav=nav, entries=lambda **x: entries(latestonly=False, **x), latestentry=lambda **x: entries(latestonly=True, **x), revcount=revcount, morevars=morevars, lessvars=lessvars) def archive(web, req, tmpl): type_ = req.form.get('type', [None])[0] allowed = web.configlist("web", "allow_archive") key = req.form['node'][0] if type_ not in web.archives: msg = 'Unsupported archive type: %s' % type_ raise ErrorResponse(HTTP_NOT_FOUND, msg) if not ((type_ in allowed or web.configbool("web", "allow" + type_, False))): msg = 'Archive type not allowed: %s' % type_ raise ErrorResponse(HTTP_FORBIDDEN, msg) reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame)) cnode = web.repo.lookup(key) arch_version = key if cnode == key or key == 'tip': arch_version = short(cnode) name = "%s-%s" % (reponame, arch_version) ctx = webutil.changectx(web.repo, req) pats = [] matchfn = None file = req.form.get('file', None) if file: pats = ['path:' + file[0]] matchfn = scmutil.match(ctx, pats, default='path') if pats: files = [f for f in ctx.manifest().keys() if matchfn(f)] if not files: raise ErrorResponse(HTTP_NOT_FOUND, 'file(s) not found: %s' % file[0]) mimetype, artype, extension, encoding = web.archive_specs[type_] headers = [ ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) ] if encoding: headers.append(('Content-Encoding', encoding)) req.headers.extend(headers) req.respond(HTTP_OK, mimetype) archival.archive(web.repo, req, cnode, artype, prefix=name, matchfn=matchfn, subrepos=web.configbool("web", "archivesubrepos")) return [] def static(web, req, tmpl): fname = req.form['file'][0] # a repo owner may set web.static in .hg/hgrc to get any file # readable by the user running the CGI script static = web.config("web", "static", None, untrusted=False) if not static: tp = web.templatepath or templater.templatepath() if isinstance(tp, str): tp = [tp] static = [os.path.join(p, 'static') for p in tp] staticfile(static, fname, req) return [] def graph(web, req, tmpl): ctx = webutil.changectx(web.repo, req) rev = ctx.rev() bg_height = 39 revcount = web.maxshortchanges if 'revcount' in req.form: revcount = int(req.form.get('revcount', [revcount])[0]) revcount = max(revcount, 1) tmpl.defaults['sessionvars']['revcount'] = revcount lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 count = len(web.repo) pos = rev start = max(0, pos - revcount + 1) end = min(count, start + revcount) pos = end - 1 uprev = min(max(0, count - 1), rev + revcount) downrev = max(0, rev - revcount) changenav = webutil.revnav(web.repo).gen(pos, revcount, count) tree = [] if start < end: revs = list(web.repo.changelog.revs(end - 1, start)) dag = graphmod.dagwalker(web.repo, revs) tree = list(graphmod.colored(dag, web.repo)) def getcolumns(tree): cols = 0 for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue cols = max(cols, max([edge[0] for edge in edges] or [0]), max([edge[1] for edge in edges] or [0])) return cols def graphdata(usetuples, **map): data = [] row = 0 for (id, type, ctx, vtx, edges) in tree: if type != graphmod.CHANGESET: continue node = str(ctx) age = templatefilters.age(ctx.date()) desc = templatefilters.firstline(ctx.description()) desc = cgi.escape(templatefilters.nonempty(desc)) user = cgi.escape(templatefilters.person(ctx.user())) branch = ctx.branch() try: branchnode = web.repo.branchtip(branch) except error.RepoLookupError: branchnode = None branch = branch, branchnode == ctx.node() if usetuples: data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(), ctx.bookmarks())) else: edgedata = [dict(col=edge[0], nextcol=edge[1], color=(edge[2] - 1) % 6 + 1, width=edge[3], bcolor=edge[4]) for edge in edges] data.append( dict(node=node, col=vtx[0], color=(vtx[1] - 1) % 6 + 1, edges=edgedata, row=row, nextrow=row + 1, desc=desc, user=user, age=age, bookmarks=webutil.nodebookmarksdict( web.repo, ctx.node()), branches=webutil.nodebranchdict(web.repo, ctx), inbranch=webutil.nodeinbranch(web.repo, ctx), tags=webutil.nodetagsdict(web.repo, ctx.node()))) row += 1 return data cols = getcolumns(tree) rows = len(tree) canvasheight = (rows + 1) * bg_height - 27 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev, lessvars=lessvars, morevars=morevars, downrev=downrev, cols=cols, rows=rows, canvaswidth=(cols + 1) * bg_height, truecanvasheight=rows * bg_height, canvasheight=canvasheight, bg_height=bg_height, jsdata=lambda **x: graphdata(True, **x), nodes=lambda **x: graphdata(False, **x), node=ctx.hex(), changenav=changenav) def _getdoc(e): doc = e[0].__doc__ if doc: doc = _(doc).split('\n')[0] else: doc = _('(no help text available)') return doc def help(web, req, tmpl): from mercurial import commands # avoid cycle topicname = req.form.get('node', [None])[0] if not topicname: def topics(**map): for entries, summary, _ in helpmod.helptable: yield {'topic': entries[0], 'summary': summary} early, other = [], [] primary = lambda s: s.split('|')[0] for c, e in commands.table.iteritems(): doc = _getdoc(e) if 'DEPRECATED' in doc or c.startswith('debug'): continue cmd = primary(c) if cmd.startswith('^'): early.append((cmd[1:], doc)) else: other.append((cmd, doc)) early.sort() other.sort() def earlycommands(**map): for c, doc in early: yield {'topic': c, 'summary': doc} def othercommands(**map): for c, doc in other: yield {'topic': c, 'summary': doc} return tmpl('helptopics', topics=topics, earlycommands=earlycommands, othercommands=othercommands, title='Index') u = webutil.wsgiui() u.verbose = True try: doc = helpmod.help_(u, topicname) except error.UnknownCommand: raise ErrorResponse(HTTP_NOT_FOUND) return tmpl('help', topic=topicname, doc=doc)