From d7407aec00fe2c69bed7933192d09770dd73e7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sampo=20Kivist=C3=B6?= Date: Fri, 6 Mar 2026 15:59:37 +0200 Subject: [PATCH] updated icon to git comet --- assets/gitcomet-512.png | Bin 0 -> 46878 bytes assets/gitcomet-512.svg | 7 + assets/gitcomet_logo_window.png | Bin 0 -> 840 bytes assets/gitcomet_logo_window.svg | 3 + assets/gitgpui_logo.png | Bin 27605 -> 0 bytes assets/gitgpui_logo.svg | 83 ----- assets/gitgpui_logo_window.svg | 76 ---- .../hicolor/scalable/apps/gitcomet-512.svg | 7 + .../linux/hicolor/scalable/apps/gitgpui.svg | 83 ----- crates/gitgpui-git-gix/src/repo/blame.rs | 72 ++++ crates/gitgpui-git-gix/src/util.rs | 348 +++++++++++++++++- .../tests/fixtures/gitpython/README.md | 18 + .../tests/fixtures/gitpython/blame | 131 +++++++ .../tests/fixtures/gitpython/blame_binary | Bin 0 -> 14807 bytes .../fixtures/gitpython/blame_complex_revision | 177 +++++++++ .../gitpython/diff_change_in_type_raw | 1 + .../fixtures/gitpython/diff_copied_mode_raw | 1 + .../fixtures/gitpython/diff_file_with_colon | Bin 0 -> 351 bytes .../fixtures/gitpython/diff_file_with_spaces | 7 + .../tests/fixtures/gitpython/diff_index_raw | 1 + .../gitpython/diff_patch_unsafe_paths | 89 +++++ .../tests/fixtures/gitpython/diff_raw_binary | 1 + .../tests/fixtures/gitpython/diff_rename | 12 + .../tests/fixtures/gitpython/diff_rename_raw | 1 + .../for_each_ref_with_path_component | Bin 0 -> 84 bytes .../fixtures/gitpython/rev_list_commit_stats | 7 + .../tests/fixtures/gitpython/rev_list_single | 7 + .../uncommon_branch_prefix_FETCH_HEAD | 6 + .../assets/icons/gitgpui_mark.svg | 15 +- crates/gitgpui-ui-gpui/src/assets.rs | 12 +- crates/gitgpui-ui-gpui/src/view/chrome.rs | 6 +- .../src/view/linux_desktop_integration.rs | 6 +- scripts/install-linux.sh | 8 +- scripts/uninstall-linux.sh | 2 +- 34 files changed, 912 insertions(+), 275 deletions(-) create mode 100644 assets/gitcomet-512.png create mode 100644 assets/gitcomet-512.svg create mode 100644 assets/gitcomet_logo_window.png create mode 100644 assets/gitcomet_logo_window.svg delete mode 100644 assets/gitgpui_logo.png delete mode 100644 assets/gitgpui_logo.svg delete mode 100644 assets/gitgpui_logo_window.svg create mode 100644 assets/linux/hicolor/scalable/apps/gitcomet-512.svg delete mode 100644 assets/linux/hicolor/scalable/apps/gitgpui.svg create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/README.md create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/blame create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_binary create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_complex_revision create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_change_in_type_raw create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_copied_mode_raw create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_colon create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_spaces create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_index_raw create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_patch_unsafe_paths create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_raw_binary create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename_raw create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/for_each_ref_with_path_component create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/rev_list_commit_stats create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/rev_list_single create mode 100644 crates/gitgpui-git-gix/tests/fixtures/gitpython/uncommon_branch_prefix_FETCH_HEAD diff --git a/assets/gitcomet-512.png b/assets/gitcomet-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d20a7fe798d47b7e49b7827297c055c8df7bf06b GIT binary patch literal 46878 zcmce8bySpF`}PbW3R2QYS%9P>-5yG5L`qV+yK|JIz=O9Lr{&9);2(Hy|2%$r89e@%%|pTezT*5y&kY76X@vd*Z*|Hw2mdFXyPU4Orjw<+ z*K=13n3tCqkBy_9o7r<`3mzv|tK=JUO|ilp17rR}z)^ z)+eW!mnwTcRC6ec#k4PBdTK0LMGk`L?k)NC95KnEJq*zgWiK8ZGnC6#t)XWCzT1%_1k>anV+Fcn;JCPN)c7G5}s|uWtp50X5oSdz$ z@}P+FtWFt#R(M@bDcpO!2p8s#>EvzRv`7zNB_vD_$YxK`yk^8WLF~bVTzo$6e1Z!p8w-m&}>@t?>|&j z$V~Qzf3GB6Vpn_|rfWJ>TfLS74L4S??nwS;*rcDDy@qZTKkd~^D%l+!q>_iz!C6Xn zb{1^6kp$GRTosPHbYh;Thc{%DSylS?9>=&d{@S}j(NwL#%+2(sb_aTuY%f{uRZ5Eq zQ9}ZLhm|MN{q6z1nS*&cg+@iD{j4Kb!btFmV6lCX5}s?rMP?^^4-(xc%tbNoy}FA( zA2e6j`nIbmK(ASkdoL4kUTb}Px|obYu~(h_&g0Z6FwoOGIyZgZQ7<2kT=^Ve0!LiySgtCxL5zr7Me1^Se8- zp3W!wEG&zby!G~ccaf+`A1f$Ii!IWFc*N+kz?QJHW@VYRMqU@Pjny4;pYWJ{U%NHe zTs;dN2y${cnV$oc7AsHW;QZ^^$+9uuOTkutEDOVjiQdiv&%y$TWOlH+XhSw535cxj|E*8xm3Q1fy7a1jOTRoV}zBx=G5cM2GL$9(&& z;p7aoSU2A(qzJ%j_Tt~8F_=wg%MJC8R&u~hZNW?fvumJ7oR^pO>Z_-Fn?VFH!}{GP zTRhb}Y~iS9`GVI?_$;hws2C65$7!j<-YctGX;Xh-SGAIV?VXl*B^T9rgQ?j~T1$mh zTH~i>p1?M3Wn|Z)yL??F7<6A^_Nzuj_7O(XcwWnp^cBvFwp$-;GRQvINE(}gKXG%b zs7MQQrL9)=ScyAX!+y-XevLVK;apJ{*u%s^iOL%*A^5dt@@q3;&&r`NqW_YS_jg-? zWDg98I_z_>5tkbA1eXZ5KK?61CWM61wy45@N5^qvZ3L0Id`v79>Qz!apRL4<`t7;2 zh(jIT+Mj>jq?DS<+&^1FmznNyU%M6;$_iEgRi!30l2eEkmAoO=!CVQ^6R7_+c*^qNDfEw>>YU8W87Sy4w`SDIMo~0*fR9A)}GeOgFPyt z`}$1GYh#M}>zLm@10UYk5o9<4DYe&jcsQIV1Bw_${-RemXva&I#++UZ4Xuw!ozYr5 zOq4my$+M6h&V=+x5x7e*ki0Uta^N%y(9)v1LzyH|jlw=v&%hA4s9}BM@9&$nL|pee zUdfbb8?T%le2G)}`AJlJY;<%okuXERaRkGaVS5Qb5=O$|_2%Z_OK_xVsVGCS<~49a zdJFISUO&XhKc7x^5_H6S_N>eiGmR_VUo$!SY~-${mP3BUox_CsF9wya3)>4jOP#8+ zVc&72lkReJbEgUyeUR38DnRRh>az7ajIBc@PBenm$J1|fvXuc>m;rMf7Yid%=t*mQ z9acD<>JR3dxtp64ZgB^K|(9rva z&ZSQ@V0yB8;E|K_sXSa-Pqb>vV-)L9xf9cFUIbn}RoBWyR52m*I)a&W*nXmlnBg)E znTaZX08K4gysE$^nhLk8D_O91hh%yuV5>Pv07gba5M2DAqs)u?lt=|Jh!* zSU3%sTG4a(hc_J9crO_fAikG;zD4SCwlt^{?lm?wFl5@^vpG)ZEJ{F3FGS*8d5JXb z-ixjSD2A4QSwFMOLlb@b=6`mC(n6rC12JPx3aXse=@E2uW>Eq{ezhd6%;$%_3d{k! zL;3_v?2jGbL@)>C$Ka8Zb17;!=UX{`iC;y+-hqG@yWI)T%td9wJpBrdZpp(O589%0 z(g=s`4Xc@aKCQWL-99sV-Ke5cS}M(cyL*s0q`9(c##vKSrR&Ag&e>_Na1PDoSEqq4 z1y1+anMlQV@3@kDwMNN+sv=C3SCMvQO}6+WgE_y z-+%v36@wRgt&awsU;lF?d1QX%>Y_aJLdc*`-yy#skHdLLFHfibWMg}wGrnC_w*C!1 zf->v0un#;)q+J1=QV#7@t5uU+r*!(p@q~`9F(loeZ`w18-sAV@Xrvp8FCK}HBjy#j z^~(EnTgio?E1nxMRd0t*Fvh83y8P{1o2n8RZLDYn)fMVakd0Pw!B$r7u5(5{r;bV$ z-CHNhMNI@wcf5M~(Wa}5nAr4HgIyuqXtT=0sqQWzPJVuI|D7b=gsbB|OTPy>b(s9t z&QGHXR*(14c1dgIyKY>tPw%0mEU_h<)4%Rnt^dr(`{Aj*s-&K0fvg5)c(Wz0LBaDz zRbhDBPp-R(g4|RG8{uMyFB-a2^~;evmQu%;t!+li92zgNP>z;Zjg{?J;YEP3W_O8* zgHtQDM%Mb_3hGBO$%NpLC=?nszJ^~URR{m9wQuA}L*oWI1pET|MwsnR5cf4nkP zvSeWqGtA`HW%0#VPVY8?e|`lP^+wV$6_rg=L2{Q>RN@U=Z!a8 z#mClX-&@JuPtMmKxhqGS^V{}AQ4#9*D;mV&;+yLUT=0nNDl3mPh6>Ade_E=DZ8dd3 zk77}K-28L6bTPEa+uL;k&ELFxZ^G1L3k>hHBE^#PE4=@hu!x-;&hgd>7au55gQEJ+ zjr+aEqJaURv(0tBl<_`&zF0nsqm1de2HD# z+xR%MPhY%#eMgDO2k*}bh_e{A`Gv<{HtQ(H@K{xzmD&!X7COrv<5F{LP2tk-`ud8@ zTHjokc&5Rxf}Zl0sYy!Z7ScMURXHv$Zi%X2zS%Id+;7!%*~ce>m0NPE<&9DTj^xy0 zS7!q5%vZF&F)uF-1b*%$psEZ+L}rJVaigN*C(Ns|ELMjOrU-toCh!ET-FG?e-IVaUDo#19lwq|o3alADwNrZLB9B++U1yB5EcM+PZGb`|RP}|={&^@sHdAP-v z@?jN`UAv9+9&}6q!=P5u&^XwTR_$(QGxne|DFtY{#A>_1iEGfwaq^qZ%99VDSAt2Y zxd75R_T9J#YR}C~>qO;|5hl`UPtxmxKE>A0By8RdKb!Vs<5jrFUPd@DSi3hAZ~%XS z5H{qHz{8P}lK-=ZwqR5A0*j$*V*@P)ofSJ0G*BfyNF8qK%Fmfr> z_h(}@o5b34BW#m;4tD0Wh6-sWo?^}x7n_XJp_LeAGn?IXWenK8!Um(GyQPrk`Rvn` z=d)p!FItETTUw|%(*i{eRjDqAG%HRdKG4a3R*EX#PP8&-Q`7+?Xag`=At3>jO5G>s z%BPwuChug-iYTc)*9qt6Q#?o0&#&s{%gHr2ht(HAIFR|*I9L3(kYIrkrcWv54+T^a z4X~9T?8DFdY-yLx&1qZp*sNXw;N>(0`oPlC!dlOhb#F4;c|8aGvT}Aig{VI)V_+D z4n`YCjjy1;4QVsgV>_D9idu{$<=5NV$Rvs);)=sI!YsbPtgUtHPQ%z>g$IqKa^YKL z;ypwmQ1kns2Z9_OUX%A1wJ15MVYB8x0nX{@z69nLaS7>O zX@?Tx_pSbY)%cwS4BUBUyi(e2r{#^qfuyfYFp2|-V)C=U=)Y=SdFSfFgk_;7ih_-r zk{yY<6wK_;Vzk~X2X<>6hzT#U=vk)S0h0Z;Z;wOg%FiTe znZx!xG;h;`TIkS*2^F=NDyFMtWjK3#O7Y^y%P*+p1G}L!3c3O7AM2js*WD8n=UR>Z z?|(pP+uhV&E-MS%=~7^ZgELa&y3>`pQr1IrzIuB|fq7(IFc}p|S7JW`YE}GJ%GarW z%cv|RYB`PA>yGMLd4skdl@Vs_quotY<{fQqJp_bAR^2rx8%-*w!QUsU>(A(i%Qt62 zcxx%G9nY3O%7w#JPoYQ+C3$^VJ5nK2?=*EQ>StPNMeT{FAGQSJ-*#}+l8#c-OFopF-YkTLdQszq{y4rTFkrIA5K0a3I0sShO zWN(wN*Kc_k_BTnX+YQz1Ui&qytF<@oHr}rWkcup&8lZWSyQD2G^+uS_U=wh2i=kx*lnU3!su`+S6v7hQh<6qCk1C zpqR&ws3|M=ot~a_Nz9NwT+2iB0pF}pUG|Yb=t@<(>N3b>V(v%`*B9>U`rhi?jP^7D;&)eSMHJ3%MO!ufWc@rQ5Hb(6B$5!otfH6UNa zaca}<2=je~{UNddPb>3OX>p6W%`=qoe78$f-kUC1lSlQpLJ>N*EQ`Uev2tymj|uy_o$fN>EgD9`S%EDY{EU_s6v`?c-`0+Xt71p>Z@fK;&c??veaTa{EGUp~ zaCEvOU6bU(30mlqFrQYbvm1*y6qp%LD%$URe?_nEy!vPDii!0BCrmzyNy2x$!etI7 z*SnXc)SUNuOrp<{lZQ!|tgT0CDJ9t{a;nJ~Et8@CXndvqz^GyAWIkr0(~LO*JHK~5 z3-tWfedYiY|oAQZ95Y7E`P!iPDXLgmPXd|#B0{6$bI?OM*{HafU1f)=Jc@o?9y-if;cDp z5_+3MNq7j3F;nu-#i;GpF zCg*24X;q8#EVSdbC#!}3E$|Z^mOpb*#sT`wxHOp?wt#(ne7>8oe_}WGzhT796`TB9ijD&F z;|CFM4cJoCyIgCdV7(e=T3P_BggHqMJRY@sp`Kgiiw5JngGFCutrz_>h>C;pF((4$ z?4LiqmB(>kKpYPNDlgr87L|8XAODG_N9pi2=O*%X3nl4lJvhs=aS#aJ@kolGn0EV1 z+uPYH)f-jbs;9M+$G#@#fki?g6hY7zZW3O~%k$cZ_;PiKzp%VaaNi|gyNHTKNav>= zOiLiqlv(Kh_RgFBX>+#6@!xo15g=}N54X%(k#m@CNqx^W2u?6b1Egpo6|eiTZrBr2v-ODjk!49%dW`gIbXcnHMQ3k7F!*A_Hn< z$(QO=y(S@ICN@FmXn%oNHLBG90!G^PSjHAqk@MtHAksoNy*8{G8vqOl$vK76BbKHck=eWHu@f1 za&=`@tK01xZteFdYR|}CY^Eu!m+YGl#-7S$6Z~L6_;(3OJFF}50-p23>Wnixy*ou02elg9p zV^9aTMf`55xuq?=NfF5P#W79#qm(d3#g@p_d(RtSE()hLJLXGASrHNBw^T*$1gf0* z*L5Z9d?5=WH}<}cktObQGA0D+~Y=)c3e3iYBP4 zV3o$;o-g3Rsi+i~?ux0@d{Ti+YYA{uR;{qkC64oxx3~l*CN3;&u`AIo*$$!WF=fI7 z>h^FJ>?9N!?r#FvI`_tJ1CH}zqPpB6JYXOtso!Ismi={9!zY{k%HL>-cpvX0!uy~NaylXS*T8V{VNz|7QFIbS#;+h&~XI>+ZO$RNBU=Ril7^s&dW?!^-Qo=e%! zs3MnUWKQ}eoEOYNr@!-b<@px1*wRmPqyW(h`hOQfI)Y4!AI9e#VhZ0z4d5TXeEDL3 zdU%Fuz8Nu4xs#r#_r$e@4$<6~Arr;a8})JZyNMkqfB~a+uWS)A3A`8C)cUf#90? zlYVwPt$k|8NpNY=a}D%;5ljJ#iA7O5#`WgyAER)-n|SPXrxrzFyENHYu8d!27^zbj zKYz_mw^>xPq!9!IMV4TeZh#QfSet7O$%FyXM91i^SbK);1AN3b^teVTpy6^Nb5}=v zuIlMgup0QgtQnqR&KSi6oh~IM3ppCp2nQZr9E*4Pl|GHH%cm|~RK>aF*88gXZ5{Md zXFrJM5_q*|wRa=vp#8A0_^g?9DB+L|BfI*#5$QT&F`GESBSdUcLPbN$a2ZQ)(~@I^ zTl(HtRS8X2fMPVP^$&Jq20X)jR004EEA>Zu{;4R665aJ=6`*mN zCQ9ggz6*}q#z$a94bcHp@@j6&?%32;>y3oefYYrK%Vx=EnwnaJpy!{%cy31iO&ys} z$q5GsP7iPW8m_P%4Cif;G}0dW_X>?6Uel<%gb^eR67fd-)YsG}JY_p^04k2W=l)~1 zFstnZZNONEsHR-jXcj*pwenFEFQxA-1(c3A!l-M#!)1+YvSJQdQOkyA0n_y|bP zKq4DSX?h~XR2}IX)+HC;Q>fu7XGcVwo-Vg;9Z8;iK@kEL>-?;wXExyiq_Pjvb@k{! zt2;F9N&;J%$oF7p8+XQ}8jn*|Y!HKv{N^N07!#jY3fjN?JB zhPLa>?0;SW%^Z2D$ll$|jORz&Z0QlklGxmkS(csge5Ckj2aXlDmRs9@a&e~$5`zXy z>r4>_`sA3$=<`1WBaZ`BI;df?4_-a!iA}wA%d9E%t|ly|eZ9LoGLWJ0AehuBi1BjB z>gvJ9oBw$=>xKUH2Y?mKLH$Q&1+9sVaf5~hdcn0OXadLLh|H9LvNeW!)&)Y?ojX8q zWR@}y^&a{d@`XVjzkQua;P-$g_^|BQ)N6{~s0B@(gs;0?bF@N-()LuA0Z9FzFH@B=^d!k~`}JXK4wOD&z6T$f zTPt?34Up66_QjT#dL%9G2naOSU2D)ST6ZU*RX1X)_b&>wpztyM1m1)8U=&{G*F)=2 zg7xX$?Gg6BN1rs**Ef9GyLNS`dHW$G!2uG$>&+WLBRHlYUrK+Apc-$fQDu^Q4xbjH zRXT4b{p{zZC$hG0R4X3=`?+{}+MW85Ip?!Y7f9h$US|Py{}Z!9Ez~fGW5*}TWNtMj z-x02^wdyg83^T4<-ud)eJl=DvyRz)d@~iI}M!0C-HJm2qaX-I9-|Z2Rt6*Qrly0*szxD8# zi>&V&%2GN%>y4V*aTu@gc3D&5y2v!6*FT2&y6t4EbjHg`MgAosA+aD!>2#Zs0}|;^ zy?w3(s`|n|eERgse0x5HVatB_o}LKAn9CZ6)z(!s;%_sV<)PNe|s zHM}Tt54X1&DPE{OxVfbF#NOQyNbmQO-Q2|7VT6R;Vw)S)@bIkM+zMUK3l&}Qwwckw zj>E+9*$J4VD=~wv#*M`QOE)kF`VtYc%Xo;n$e6eW7U1kaRgtviwS&xf0_Ym?$P@kg zG_SL{o~5x0m)@Jw8eG(QRDZmAD$O?xSj7BhoAgSsZ3NHAT`cRsP7YGFeP~vB5@3(@ z^stn6Tl7|^N3Of!M!Z+>E)Jpq7y0BF$LcW;diwiZi`+TpqJDse4Reql#8hu^o0b#) zdzFSa@bI~`Jqc`lW2P}j87}Hx@f&581|Q!bzm={0_|=>wc8A!FPR};$#%ky;zp}OS z+5~G@?$<_^s1JWi1)J~kJLe7gLX?1g{KRMVwrM=_P@QoxKtO@yK3a_MUjiIauQ zBQklt{EC#|!hhb2&_t#ZV`9zWlkm zR=JoE&DW7C_mwHBV;BFr9x3BmQShZ--nDk-S~(AH%{AoX&=_--5xQ@T zmpjewK;kR8#u~)IhFLIKmV2!Vnn;@~Ra~v&DP(-!M+DQ%Q3bXll*{-O0^VL%*YDFr zJ}c$*P3b)NVLdY_EoZ(HRo*!tgDvcVrOEi4^<<6Ad0<|tS!OP^_+OJ1KYAb|y60_!;LEVGTi&4zhd?WF^r zgXJ4(BB|ihT|*^HU!V5|1=+2ZIjo{_lNJxo%O#d$4Fb>0ryR$DbmzY}7{V<7;AJL5 zg;v8{}u@J4X;BIJAJ)DNXr5}kSyMp+TG!ARNKez zvQ&AH93{#zf(H8Ni2bFt{n=UFsX+({4Z^nYmw3s`UlnUzPAhVNZx-;Ayao8KL;XNC|TXUz9lFgstr}Nto)* z&Rm!!AeV`zCNveFS|Fqz-XtR&L~oAK^h5{MX?@6DMslWk&w?m%fDHi$BvL3qW<}3BkWmSmXw5os>I_eZ6tKSw2$b6=jh|%WX*D z4i1;Ov5*ZuY#}JjDug+$X7w4Drx@f@Y3?)4$XdXXX~sN?TR`Vkc{WD^$+@^5x~rzXDILgsn~3x_mwa<$KZ)kf%rg)Ri&C zapGM-^Cv18(7{1q&_*eUD;XUG<&|o(Y3PJGhLwS)-?tXH@T_{u9JW*y!nBR+oo8f7 z;RxXAX|FD5?5#fdGb63hyO1EYh4(g5Pg^j+_Y>V6EC^`kAsa4T?c@cx2)H*$Feg;a z=6uPZctCk)Hhr_a(%=F|A04$Wli?Z5ZJK8bZe0<@aM7VtH*`#@eu-3~@73&n;>ok2l zDK27zkASR;^E9w8L*1!nBYNcEy>y)fVNYaaWGogm)#Q?1wE>QQ$?G@X{8G<*m(_f! zDGrk_Q5P{MLvi)&Ph&iHZ&3Z9@8JxeD(){2L$yV1#O(s86I&S<@j+938H!{yvk{@a z+_e8Na{zKR8Sw)AZQA^xr%jc@Wm@}1D-$jspQ2*7aIii;Han2KnqnTS>9Q1C`#SFg zzefmbUyV*^VntMuP*St5`eNNm$eq!W;!L`SXaVBOc!8%|d4;p7k>)mzlb~W|wi#tS zeQr9VX~8Lk?zV)_j!{BO_Zq z2l_fgd-J8lYR@1Y3BRpVX=u-M{h0ac5Y#q&unDa1p#ilrVT<%1!&GaM_o!ONpNr$; zk&G`+Pu;pJj}}3WD-mJK$$fNz0Wt?N0sSq>Oh$r6pYLjdo`CDstM;|7LUz#OwI{Q& zx!}Xz{=`8v&u%qs2NS*8-@t5CgEKR5?drxPW)F?%DYuDoot23m?DTuHEWUKNSqo8TW+~5_OB#;z%xGy&=$q7k5D8)|WLq+{#<`HkH*^P~T;1 z`a7c4Z6zy8k`TyJDZ+NZS*M-Xctea4TdYi*MWez!nCOTNy+mU?@^90?uGQ<0EN46N z$)Pb*)`T>KgM;f6*U$Z9lOodn0x+3!ZcFrqZK_lfa`zK|S_1J`(!Yc9Z>GZmb(iEz z?%TiXdLL4t??+J(tTA#x0HZz3zE(hG$AQxn;@o$;&S(wi+0W~Bi#dUAcG_N7m3(1pXSk}0-lPqi8c(H@(+z9oB(fh4yyiLFi<2Tij%B$b-vm4C4| zqT>Fh^e$}!ac35FU12*=L=?&hb5ULKHTaLP2|J9Okd70c`4QDVPWZNr6L#&K;6ZXp zF(F~*c_0sljm0(6+RSXsp2YiWU?sLp7N5GQyw91YqmmJNL>VP^0VP9MUT-!jk<9^y1z>@PLgij?)Aj<_x9t4 z6^47$6?08G&Y7|fhQi~&z6MFXg^1;~5$T8D?<9zy-ZLqup&_Nejg37imE82bs&uzW0Vl z34x6+@OYydF~}j=b94j_PAlW=PGFY+0wv0@a!3RRm`t?yTRb&2>`arM8fL1G$^Z#; z97npo#hPUJ*H`4etrqN%u-ViUrt?nOzF0I%Ok6caZmRFW|9x=w3J%Hue{SlN*y3`#`!Dny_%%_fBO z#khq0+MKGx#Hg9)JiY^z=P*rtRY^K_j;igB|7i?n(rj(7|C4ci9B=~K$;Q`m;Q&Ts zfjLC|t72A-Z*k&M0z3CuOgbbXoy8QiiWY`D7$u_m3Sh8YTYD?w)6+=OUMGOXYrHoM zu!8p^&!hn}`}X#3$+JVf5`f1OA8gjr0DGqA+K8s7Xy(+=aE0%l60e;0*(tr51?Ux; zX{7!jgatk-CCo$oWajy&)eLp69Z_I3;*LLDcuWQMD|C;5YL(#CXo89JRNZDhLCs_- zkkmlR*@H^s!8#EorGa}dsN^FfSW&*{V1q>wPh|&~sDU2{GJG5#)K3cY^dI_*r3PPm z!<@bx3(ni_)N)mJvum)bhB7PgnEtD)0|NywWB+rJ%R&c@QK&mPFgpNvUmZf6EDkN- zEHWnz$DklXhD~Kw8?FdUL9dBH78GUi>y$%%UUxjM66lGnD(H48<;eyvgsUG0&!)$K&zYv9n?(ziwQLR*ZoVrl z*jO)T_E%5gm^tWF?f0b9hZ_{!kcpI%EY@le){^#rwH7-t3E0k)jglUDLY$C}WuNHu z^AM?V&+BiaHJc1`!ghv}M~jT(x3SSHRCE48*Y;J`Hg2@`we;Xy_{$%1gnDXVlXt#;LzGwdO~g^&3017s4RD2ls^ zlqy6aFGc}Fv{9wRAT}^cf08TT-+6jU_W(d@=gOv{M5x;I0<%1O7_g~v`Rr<7Ni)k4 zn^Ds6tgm0~bX-skhF);2jbZN1g$05gtOSu+o>~BAws}jOWI*@(yBN10R}zd#fRf|; zTLAr42L=YoC<@m2-6P1_Jo<>nZSO>{D z1GNhx&JR_W5b%WskiILICS3@C$p@6;)^kxX*4RK@7|XkcRm`78p((tV-$4pqybeao zD_8W4k%WcQZoGnsCzKLzZ9h#SO7a`t}t=(gh3Oe2e(z-nrR^ehAS;@`6Uv@k& zRJYy1T@I^aB2n-%n0C?xM8huOd&qThtCbKqcsLYK3n9SZKtlwRy@rn?b#^$YgMd^p zrT8{gnR<5!Lo=Trea6wwTXP@KrVZ-3emxJB=EoDQb1mn&xp{1?ax!ZY!K6!wHS7DB zn6_20{~G$=L2MEJXXR3{b9A(RoBBHNu~xozGWQc0$WUt#bqj%Jv&iVw$Wg)zeFm9A z_|6(aXALiAg};X|A?@ivbDg;#jt|SEyl5&vkx75$HpuYntEzy{z`Wxp7ds5{*qkm&-QvUp$siX4t`@Ps;ui`!-72;K zrVX>!#jfK1U*D1TR292@M?0kB!45^k>mZ$>?z5gj>dNU(I_$%0`wBc0#i}{K1Axe4 zBHEag9oe<$dS~t!Ea2_yU;*#SDdPd6izh`)$VH`*{eFbo$Q!XQa&kHbTTK**ei3IA zxmRoMv9>_vfXropV9xiOdoyo5H6uJ5s__vPwKN;f>n-f=235HLd-Vm`lkLp`twWSZev}GFJCs2CZt9_kqf{STSQ6mZ0f3)h)C6IvVHja zCo`PM8|Ga3`l`s;;rtS`o=s-Z{OJrEFZ6kfoDAKOi<&vN#W6a@t?zNr=|}IHZN)TS&;`b-g1}UKjx{&Kh>0N& zRqh%;r4(ySGY*K6_%t6@5YH~nnrbe2(tb3AS~{EKR|W>dxTT9L4P;&`r(C9_Hu9bc zIFL&+h5~4;83fREz@v>6`wILT1;*ypp>|c7ElccC17H+X1h2_dFK~zC`Ezsu*s0

x*Nzv>wXBA*>tPKj}S-;U9EvnoDeJZ%})@knqCf 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 literal 0 HcmV?d00001 diff --git a/assets/gitcomet-512.svg b/assets/gitcomet-512.svg new file mode 100644 index 0000000..6e813b5 --- /dev/null +++ b/assets/gitcomet-512.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/assets/gitcomet_logo_window.png b/assets/gitcomet_logo_window.png new file mode 100644 index 0000000000000000000000000000000000000000..c4378dc8d2e4a8c307837104d0e6ed3d7171120d GIT binary patch literal 840 zcmV-O1GoH%P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10>w#0K~zYI#Z^s6R8bWE&V8cR45AQjgaKDBL=kl1-%O7}hzLQ6 zf!>S*(`FRhv~VLzKf78FgqwcEkthgp6PC#MR9uKK_yZ2oLfS=)E~N5j?$_e-45No- zQQzjgd+t5weCOkEo{%Jo{|4oKIF9QAsZ=82xS`>uumFsX zUcdh44Nz9*y^BVdmw^ix;&Imn8XEo@ffp~XTmjy{2S_rVHuk}Ts;WSMTwiybY!QnFm`PX z07P!y%w!@FV0+t-fbzSjtGj#|c>dg+ayg%Hb-J*J#zp`jS61Tj#YN!8jWY=tsYPzx z%4UI`?6 zm@apA%qu@BUH9lnYmwU8OeP!_35O>qBN35?29gx%=`pzqUx1a&FFetE*4HIykVbk`j?nXnefA9r*asPaDTsSkU_6L#0TO&d!O6NJONg!*>QI zCp$WT%}vYFTIA|gN4I! zK|}-yg`&|{uK>$BE03A6%1U!OJp5$>9x@r*268!4M5MRZc!R-`lIm(8mGX;ACbLLKL79oMACUaXT7=qK&Wo23`b^pFs_>UF%TlfaUwO2>J SCJ)^J0000 + + diff --git a/assets/gitgpui_logo.png b/assets/gitgpui_logo.png deleted file mode 100644 index 46756af593155b32fc1f22dd77625a452338c9f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27605 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ⅆ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 diff --git a/assets/gitgpui_logo.svg b/assets/gitgpui_logo.svg deleted file mode 100644 index 538efd2..0000000 --- a/assets/gitgpui_logo.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - gitgpui - - - - - - - - - gitgpui - - - - - - diff --git a/assets/gitgpui_logo_window.svg b/assets/gitgpui_logo_window.svg deleted file mode 100644 index 822cc04..0000000 --- a/assets/gitgpui_logo_window.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - gitgpui - - - - - - - - gitgpui - - - - - - diff --git a/assets/linux/hicolor/scalable/apps/gitcomet-512.svg b/assets/linux/hicolor/scalable/apps/gitcomet-512.svg new file mode 100644 index 0000000..6e813b5 --- /dev/null +++ b/assets/linux/hicolor/scalable/apps/gitcomet-512.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/assets/linux/hicolor/scalable/apps/gitgpui.svg b/assets/linux/hicolor/scalable/apps/gitgpui.svg deleted file mode 100644 index 538efd2..0000000 --- a/assets/linux/hicolor/scalable/apps/gitgpui.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - gitgpui - - - - - - - - - gitgpui - - - - - - diff --git a/crates/gitgpui-git-gix/src/repo/blame.rs b/crates/gitgpui-git-gix/src/repo/blame.rs index 74ee01e..a3210cb 100644 --- a/crates/gitgpui-git-gix/src/repo/blame.rs +++ b/crates/gitgpui-git-gix/src/repo/blame.rs @@ -255,3 +255,75 @@ fn parse_git_blame_porcelain(output: &str) -> Vec { out } + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::BTreeSet; + + const GITPY_BLAME: &str = include_str!("../../tests/fixtures/gitpython/blame"); + const GITPY_BLAME_COMPLEX_REVISION: &str = + include_str!("../../tests/fixtures/gitpython/blame_complex_revision"); + const GITPY_BLAME_BINARY: &[u8] = include_bytes!("../../tests/fixtures/gitpython/blame_binary"); + + #[test] + fn parse_git_blame_porcelain_handles_gitpython_blame_fixture() { + let parsed = parse_git_blame_porcelain(GITPY_BLAME); + assert_eq!(parsed.len(), 25); + + let first = parsed + .first() + .expect("fixture should parse at least one line"); + assert_eq!(first.commit_id, "634396b2f541a9f2d58b00be1a07f0c358b999b3"); + assert_eq!(first.author, "Tom Preston-Werner"); + assert_eq!(first.author_time_unix, Some(1_191_997_100)); + assert_eq!(first.summary, "initial grit setup"); + assert_eq!( + first.line, + "$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed" + ); + + let second = parsed.get(1).expect("fixture should parse second line"); + assert_eq!(second.commit_id, first.commit_id); + assert_eq!(second.author, first.author); + assert_eq!(second.author_time_unix, first.author_time_unix); + assert_eq!(second.summary, first.summary); + assert!(second.line.is_empty()); + } + + #[test] + fn parse_git_blame_porcelain_handles_gitpython_complex_revision_fixture() { + let parsed = parse_git_blame_porcelain(GITPY_BLAME_COMPLEX_REVISION); + assert_eq!(parsed.len(), 83); + + let unique_commits = parsed + .iter() + .map(|line| line.commit_id.as_str()) + .collect::>(); + assert_eq!(unique_commits.len(), 1); + + let first = parsed.first().expect("complex fixture should parse"); + assert_eq!(first.commit_id, "e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e"); + assert_eq!(first.author, "Sebastian Thiel"); + assert_eq!(first.summary, "Fixed PY3 support."); + assert_eq!(first.line, "## GitPython"); + } + + #[test] + fn parse_git_blame_porcelain_handles_gitpython_binary_fixture() { + let raw = String::from_utf8_lossy(GITPY_BLAME_BINARY); + let parsed = parse_git_blame_porcelain(&raw); + assert_eq!(parsed.len(), 9); + + let unique_commits = parsed + .iter() + .map(|line| line.commit_id.as_str()) + .collect::>(); + assert_eq!(unique_commits.len(), 2); + + let first = parsed.first().expect("binary fixture should parse"); + assert_eq!(first.author, "Sebastian Thiel"); + assert_eq!(first.summary, "binary"); + assert!(parsed.iter().any(|line| line.line.contains("hi"))); + } +} diff --git a/crates/gitgpui-git-gix/src/util.rs b/crates/gitgpui-git-gix/src/util.rs index dba6fc7..2af476e 100644 --- a/crates/gitgpui-git-gix/src/util.rs +++ b/crates/gitgpui-git-gix/src/util.rs @@ -179,7 +179,7 @@ pub(crate) fn parse_git_log_pretty_records(output: &str) -> LogPage { } pub(crate) fn parse_name_status_line(line: &str) -> Option { - let line = line.trim(); + let line = line.trim_end_matches(&['\n', '\r'][..]); if line.is_empty() { return None; } @@ -206,7 +206,6 @@ pub(crate) fn parse_name_status_line(line: &str) -> Option { } _ => parts.next().unwrap_or_default(), }; - let path = path.trim(); if path.is_empty() { return None; } @@ -275,6 +274,133 @@ pub(crate) fn parse_remote_branches(output: &str) -> Vec { mod tests { use super::*; + const GITPY_FOR_EACH_REF_WITH_PATH_COMPONENT: &[u8] = + include_bytes!("../tests/fixtures/gitpython/for_each_ref_with_path_component"); + const GITPY_DIFF_FILE_WITH_COLON: &[u8] = + include_bytes!("../tests/fixtures/gitpython/diff_file_with_colon"); + const GITPY_DIFF_FILE_WITH_SPACES: &str = + include_str!("../tests/fixtures/gitpython/diff_file_with_spaces"); + const GITPY_DIFF_RENAME: &str = include_str!("../tests/fixtures/gitpython/diff_rename"); + const GITPY_DIFF_CHANGE_IN_TYPE_RAW: &str = + include_str!("../tests/fixtures/gitpython/diff_change_in_type_raw"); + const GITPY_DIFF_COPIED_MODE_RAW: &str = + include_str!("../tests/fixtures/gitpython/diff_copied_mode_raw"); + const GITPY_DIFF_RENAME_RAW: &str = include_str!("../tests/fixtures/gitpython/diff_rename_raw"); + const GITPY_DIFF_RAW_BINARY: &str = include_str!("../tests/fixtures/gitpython/diff_raw_binary"); + const GITPY_DIFF_INDEX_RAW: &str = include_str!("../tests/fixtures/gitpython/diff_index_raw"); + const GITPY_DIFF_PATCH_UNSAFE_PATHS: &[u8] = + include_bytes!("../tests/fixtures/gitpython/diff_patch_unsafe_paths"); + const GITPY_UNCOMMON_BRANCH_PREFIX_FETCH_HEAD: &str = + include_str!("../tests/fixtures/gitpython/uncommon_branch_prefix_FETCH_HEAD"); + const GITPY_REV_LIST_SINGLE: &str = include_str!("../tests/fixtures/gitpython/rev_list_single"); + const GITPY_REV_LIST_COMMIT_STATS: &str = + include_str!("../tests/fixtures/gitpython/rev_list_commit_stats"); + + fn gitpython_raw_to_name_status_line(raw: &str) -> String { + let mut parts = raw.split_whitespace(); + let _old_mode = parts.next().expect("raw fixture old mode"); + let _new_mode = parts.next().expect("raw fixture new mode"); + let _old_sha = parts.next().expect("raw fixture old sha"); + let _new_sha = parts.next().expect("raw fixture new sha"); + let status = parts.next().expect("raw fixture status"); + let first_path = parts.next().expect("raw fixture path"); + + if status.starts_with('R') || status.starts_with('C') { + let second_path = parts.next().expect("raw fixture second path"); + format!("{status}\t{first_path}\t{second_path}") + } else { + format!("{status}\t{first_path}") + } + } + + fn gitpython_patch_b_paths(patch_bytes: &[u8]) -> Vec { + let text = String::from_utf8_lossy(patch_bytes); + let mut out = Vec::new(); + for line in text.lines() { + let Some(rest) = line.strip_prefix("+++ ") else { + continue; + }; + if rest == "/dev/null" { + continue; + } + if let Some(path) = rest.strip_prefix("b/") { + out.push(path.to_string()); + } else if let Some(quoted) = rest.strip_prefix("\"b/") { + let path = quoted + .strip_suffix('\"') + .expect("quoted +++ line should have trailing quote"); + out.push(path.to_string()); + } + } + out + } + + fn gitpython_fetch_head_to_remote_ref_output(fetch_head: &str, remote: &str) -> String { + let mut out = String::new(); + for line in fetch_head.lines() { + let Some((sha, rest)) = line.split_once('\t') else { + continue; + }; + let sha = sha.trim(); + if sha.is_empty() { + continue; + } + let Some(start) = rest.find("'refs/") else { + continue; + }; + let refs_and_after = &rest[start + 1..]; + let Some((full_ref, _)) = refs_and_after.split_once('\'') else { + continue; + }; + let short_ref = full_ref.strip_prefix("refs/").unwrap_or(full_ref); + out.push_str(remote); + out.push('/'); + out.push_str(short_ref); + out.push('\t'); + out.push_str(sha); + out.push('\n'); + } + out + } + + fn gitpython_rev_list_fixture_to_pretty_record(fixture: &str) -> String { + let id = fixture + .lines() + .find_map(|line| line.strip_prefix("commit ")) + .expect("rev-list fixture should contain commit id") + .trim(); + + let parents = fixture + .lines() + .filter_map(|line| line.strip_prefix("parent ")) + .map(str::trim) + .collect::>() + .join(" "); + + let author_line = fixture + .lines() + .find(|line| line.starts_with("author ")) + .expect("rev-list fixture should contain author line"); + let author = author_line + .strip_prefix("author ") + .and_then(|line| line.split_once(" <").map(|(name, _)| name)) + .expect("author line should include actor and email"); + let time = author_line + .split_whitespace() + .rev() + .nth(1) + .expect("author line should contain unix timestamp") + .trim(); + + let summary = fixture + .lines() + .find_map(|line| line.strip_prefix(" ")) + .unwrap_or_default() + .trim(); + + format!("{id}\x1f{parents}\x1f{author}\x1f{time}\x1f{summary}\x1e") + } + #[test] fn parse_remote_branches_splits_and_skips_head() { let output = @@ -308,4 +434,222 @@ mod tests { SystemTime::UNIX_EPOCH + Duration::from_secs(1) ); } + + #[test] + fn parse_remote_branches_handles_path_components_from_gitpython_fixture() { + let raw = std::str::from_utf8(GITPY_FOR_EACH_REF_WITH_PATH_COMPONENT) + .expect("fixture should be valid UTF-8"); + let mut fields = raw.trim().split('\0'); + let full_ref = fields.next().expect("refname field"); + let oid = fields.next().expect("object id field"); + let short = full_ref + .strip_prefix("refs/heads/") + .expect("heads ref prefix in fixture"); + + let output = format!("origin/{short}\t{oid}\norigin/HEAD\tdeadbeef\n"); + let branches = parse_remote_branches(&output); + + assert_eq!(branches.len(), 1); + assert_eq!(branches[0].remote, "origin"); + assert_eq!(branches[0].name, "refactoring/feature1"); + assert_eq!(branches[0].target, CommitId(oid.to_string())); + } + + #[test] + fn parse_git_log_pretty_records_parses_single_commit_from_gitpython_fixture() { + let output = gitpython_rev_list_fixture_to_pretty_record(GITPY_REV_LIST_SINGLE); + let page = parse_git_log_pretty_records(&output); + + assert_eq!(page.commits.len(), 1); + assert!(page.next_cursor.is_none()); + let commit = &page.commits[0]; + assert_eq!( + commit.id, + CommitId("4c8124ffcf4039d292442eeccabdeca5af5c5017".to_string()) + ); + assert_eq!( + commit.parent_ids, + vec![CommitId( + "634396b2f541a9f2d58b00be1a07f0c358b999b3".to_string() + )] + ); + assert_eq!(commit.author, "Tom Preston-Werner"); + assert_eq!(commit.summary, "implement Grit#heads"); + assert_eq!( + commit.time, + SystemTime::UNIX_EPOCH + Duration::from_secs(1_191_999_972) + ); + } + + #[test] + fn parse_git_log_pretty_records_parses_multiple_gitpython_fixtures() { + let output = format!( + "{}{}", + gitpython_rev_list_fixture_to_pretty_record(GITPY_REV_LIST_SINGLE), + gitpython_rev_list_fixture_to_pretty_record(GITPY_REV_LIST_COMMIT_STATS) + ); + let page = parse_git_log_pretty_records(&output); + + assert_eq!(page.commits.len(), 2); + assert!(page.next_cursor.is_none()); + + assert_eq!( + page.commits[1].id, + CommitId("634396b2f541a9f2d58b00be1a07f0c358b999b3".to_string()) + ); + assert!(page.commits[1].parent_ids.is_empty()); + assert_eq!(page.commits[1].author, "Tom Preston-Werner"); + assert_eq!(page.commits[1].summary, "initial grit setup"); + assert_eq!( + page.commits[1].time, + SystemTime::UNIX_EPOCH + Duration::from_secs(1_191_997_100) + ); + } + + #[test] + fn parse_remote_branches_handles_pull_ref_prefixes_from_gitpython_fixture() { + let mut output = gitpython_fetch_head_to_remote_ref_output( + GITPY_UNCOMMON_BRANCH_PREFIX_FETCH_HEAD, + "origin", + ); + output.push_str("origin/HEAD\tdeadbeef\n"); + let branches = parse_remote_branches(&output); + + let names = branches.iter().map(|b| b.name.as_str()).collect::>(); + assert_eq!( + names, + vec![ + "pull/1/head", + "pull/1/merge", + "pull/2/head", + "pull/2/merge", + "pull/3/head", + "pull/3/merge", + ] + ); + assert_eq!(branches.len(), 6); + assert_eq!( + branches[0].target, + CommitId("c2e3c20affa3e2b61a05fdc9ee3061dd416d915e".to_string()) + ); + } + + #[test] + fn parse_name_status_line_handles_colon_paths_from_gitpython_fixture() { + let raw = String::from_utf8_lossy(GITPY_DIFF_FILE_WITH_COLON); + let colon_path = raw + .split('\0') + .find(|segment| segment.contains("file with : colon.txt")) + .expect("fixture contains colon path") + .trim(); + + let parsed = parse_name_status_line(&format!("M\t{colon_path}")) + .expect("name-status line with colon path should parse"); + + assert_eq!(parsed.path, PathBuf::from("file with : colon.txt")); + assert_eq!(parsed.kind, FileStatusKind::Modified); + } + + #[test] + fn parse_name_status_line_handles_space_paths_from_gitpython_fixture() { + let added_path = GITPY_DIFF_FILE_WITH_SPACES + .lines() + .find_map(|line| line.strip_prefix("+++ b/")) + .expect("fixture contains +++ path line") + .trim(); + + let parsed = parse_name_status_line(&format!("A\t{added_path}")) + .expect("name-status line with spaces should parse"); + + assert_eq!(parsed.path, PathBuf::from("file with spaces")); + assert_eq!(parsed.kind, FileStatusKind::Added); + } + + #[test] + fn parse_name_status_line_handles_unicode_rename_from_gitpython_fixture() { + let from = GITPY_DIFF_RENAME + .lines() + .find_map(|line| line.strip_prefix("rename from ")) + .expect("fixture contains rename-from line") + .trim(); + let to = GITPY_DIFF_RENAME + .lines() + .find_map(|line| line.strip_prefix("rename to ")) + .expect("fixture contains rename-to line") + .trim(); + + let parsed = parse_name_status_line(&format!("R100\t{from}\t{to}")) + .expect("rename name-status line should parse"); + + assert_eq!(parsed.path, PathBuf::from("müller")); + assert_eq!(parsed.kind, FileStatusKind::Renamed); + } + + #[test] + fn parse_name_status_line_handles_copy_status_from_gitpython_raw_fixture() { + let line = gitpython_raw_to_name_status_line(GITPY_DIFF_COPIED_MODE_RAW.trim()); + let parsed = parse_name_status_line(&line).expect("copied raw status should parse"); + + assert_eq!(parsed.path, PathBuf::from("test2.txt")); + assert_eq!(parsed.kind, FileStatusKind::Added); + } + + #[test] + fn parse_name_status_line_maps_type_change_to_modified_from_gitpython_raw_fixture() { + let line = gitpython_raw_to_name_status_line(GITPY_DIFF_CHANGE_IN_TYPE_RAW.trim()); + let parsed = parse_name_status_line(&line).expect("type-change raw status should parse"); + + assert_eq!(parsed.path, PathBuf::from("this")); + assert_eq!(parsed.kind, FileStatusKind::Modified); + } + + #[test] + fn parse_name_status_line_handles_raw_rename_from_gitpython_fixture() { + let line = gitpython_raw_to_name_status_line(GITPY_DIFF_RENAME_RAW.trim()); + let parsed = parse_name_status_line(&line).expect("rename raw status should parse"); + + assert_eq!(parsed.path, PathBuf::from("that")); + assert_eq!(parsed.kind, FileStatusKind::Renamed); + } + + #[test] + fn parse_name_status_line_handles_raw_binary_modified_from_gitpython_fixture() { + let line = gitpython_raw_to_name_status_line(GITPY_DIFF_RAW_BINARY.trim()); + let parsed = parse_name_status_line(&line).expect("binary raw status should parse"); + + assert_eq!(parsed.path, PathBuf::from("rps")); + assert_eq!(parsed.kind, FileStatusKind::Modified); + } + + #[test] + fn parse_name_status_line_preserves_single_space_path_from_gitpython_raw_fixture() { + let raw = GITPY_DIFF_INDEX_RAW.trim_end_matches('\n'); + let status_start = raw + .find(" D\t") + .map(|ix| ix + 1) + .expect("fixture should contain deleted status with tab-separated path"); + let line = &raw[status_start..]; + + let parsed = parse_name_status_line(line).expect("single-space path should parse"); + assert_eq!(parsed.path, PathBuf::from(" ")); + assert_eq!(parsed.kind, FileStatusKind::Deleted); + } + + #[test] + fn parse_name_status_line_preserves_unsafe_paths_from_gitpython_patch_fixture() { + let paths = gitpython_patch_b_paths(GITPY_DIFF_PATCH_UNSAFE_PATHS); + assert!(paths.iter().any(|p| p == "path/ starting with a space")); + assert!(paths.iter().any(|p| p == "path/ending in a space ")); + assert!(paths.iter().any(|p| p == r#"path/with\ttab"#)); + assert!(paths.iter().any(|p| p == r#"path/with\nnewline"#)); + assert!(paths.iter().any(|p| p == "path/with spaces")); + assert!(paths.iter().any(|p| p == "path/with-question-mark?")); + + for path in paths { + let parsed = parse_name_status_line(&format!("A\t{path}")) + .expect("unsafe path from fixture should parse"); + assert_eq!(parsed.path, PathBuf::from(&path)); + assert_eq!(parsed.kind, FileStatusKind::Added); + } + } } diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/README.md b/crates/gitgpui-git-gix/tests/fixtures/gitpython/README.md new file mode 100644 index 0000000..1d416bc --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/README.md @@ -0,0 +1,18 @@ +# GitPython Fixture Imports + +These files are copied from `GitPython/test/fixtures` and are used in +`gitgpui-git-gix` parser tests to validate edge cases that also matter for +gitgpui: + +- ref names with path components (`for_each_ref_with_path_component`) +- paths containing spaces (`diff_file_with_spaces`) +- paths containing `:` (`diff_file_with_colon`) +- unicode rename paths (`diff_rename`) +- additional raw status kinds (`diff_copied_mode_raw`, `diff_change_in_type_raw`, + `diff_rename_raw`, `diff_raw_binary`, `diff_index_raw`) +- unsafe/quoted path variants from patch output (`diff_patch_unsafe_paths`) +- uncommon pull-style ref prefixes (`uncommon_branch_prefix_FETCH_HEAD`) +- commit metadata fixtures used for log pretty-format parsing (`rev_list_single`, + `rev_list_commit_stats`) +- `git blame --line-porcelain` parsing (`blame`, `blame_complex_revision`, + `blame_binary`) diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame b/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame new file mode 100644 index 0000000..10c141d --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame @@ -0,0 +1,131 @@ +634396b2f541a9f2d58b00be1a07f0c358b999b3 1 1 7 +author Tom Preston-Werner +author-mail +author-time 1191997100 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1191997100 +committer-tz -0700 +filename lib/grit.rb +summary initial grit setup +boundary + $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed +634396b2f541a9f2d58b00be1a07f0c358b999b3 2 2 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 3 3 + # core +634396b2f541a9f2d58b00be1a07f0c358b999b3 4 4 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 5 5 + # stdlib +634396b2f541a9f2d58b00be1a07f0c358b999b3 6 6 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 7 7 + # internal requires +3b1930208a82457747d76729ae088e90edca4673 8 8 1 +author Tom Preston-Werner +author-mail +author-time 1192267241 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192267241 +committer-tz -0700 +filename lib/grit.rb +summary big refactor to do lazy loading + require 'grit/lazy' +4c8124ffcf4039d292442eeccabdeca5af5c5017 8 9 1 +author Tom Preston-Werner +author-mail +author-time 1191999972 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1191999972 +committer-tz -0700 +filename lib/grit.rb +summary implement Grit#heads + require 'grit/errors' +d01a4cfad6ea50285c4710243e3cbe019d381eba 9 10 1 +author Tom Preston-Werner +author-mail +author-time 1192032303 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192032303 +committer-tz -0700 +filename lib/grit.rb +summary convert to Grit module, refactor to be more OO + require 'grit/git' +4c8124ffcf4039d292442eeccabdeca5af5c5017 9 11 1 + require 'grit/head' +a47fd41f3aa4610ea527dcc1669dfdb9c15c5425 10 12 1 +author Tom Preston-Werner +author-mail +author-time 1192002639 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192002639 +committer-tz -0700 +filename lib/grit.rb +summary add more comments throughout + require 'grit/commit' +b17b974691f0a26f26908495d24d9c4c718920f8 13 13 1 +author Tom Preston-Werner +author-mail +author-time 1192271832 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192271832 +committer-tz -0700 +filename lib/grit.rb +summary started implementing Tree + require 'grit/tree' +74fd66519e983a0f29e16a342a6059dbffe36020 14 14 1 +author Tom Preston-Werner +author-mail +author-time 1192317005 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192317005 +committer-tz -0700 +filename lib/grit.rb +summary add Blob + require 'grit/blob' +d01a4cfad6ea50285c4710243e3cbe019d381eba 12 15 1 + require 'grit/repo' +634396b2f541a9f2d58b00be1a07f0c358b999b3 9 16 1 + +d01a4cfad6ea50285c4710243e3cbe019d381eba 14 17 1 + module Grit +b6e1b765e0c15586a2c5b9832854f95defd71e1f 18 18 6 +author Tom Preston-Werner +author-mail +author-time 1192860483 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192860483 +committer-tz -0700 +filename lib/grit.rb +summary implement Repo.init_bare + class << self +b6e1b765e0c15586a2c5b9832854f95defd71e1f 19 19 + attr_accessor :debug +b6e1b765e0c15586a2c5b9832854f95defd71e1f 20 20 + end +b6e1b765e0c15586a2c5b9832854f95defd71e1f 21 21 + +b6e1b765e0c15586a2c5b9832854f95defd71e1f 22 22 + self.debug = false +b6e1b765e0c15586a2c5b9832854f95defd71e1f 23 23 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 11 24 2 + VERSION = '1.0.0' +634396b2f541a9f2d58b00be1a07f0c358b999b3 12 25 + end \ No newline at end of file diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_binary b/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_binary new file mode 100644 index 0000000000000000000000000000000000000000..db78205b6a690ebe5a8a99ac5b9844c8ce2d3d86 GIT binary patch literal 14807 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 literal 0 HcmV?d00001 diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_complex_revision b/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_complex_revision new file mode 100644 index 0000000..e2de6d3 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/blame_complex_revision @@ -0,0 +1,177 @@ +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 1 1 83 +author Sebastian Thiel +author-mail +author-time 1420715996 +author-tz +0100 +committer Sebastian Thiel +committer-mail +committer-time 1420716149 +committer-tz +0100 +summary Fixed PY3 support. +boundary +filename README.md + ## GitPython +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 2 2 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 3 3 + GitPython is a python library used to interact with git repositories, high-level like git-porcelain, or low-level like git-plumbing. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 4 4 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 5 5 + It provides abstractions of git objects for easy access of repository data, and additionally allows you to access the git repository more directly using either a pure python implementation, or the faster, but more resource intensive git command implementation. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 6 6 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 7 7 + The object database implementation is optimized for handling large quantities of objects and large datasets, which is achieved by using low-level structures and data streaming. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 8 8 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 9 9 + ### REQUIREMENTS +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 10 10 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 11 11 + * Git ( tested with 1.8.3.4 ) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 12 12 + * Python Nose - used for running the tests +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 13 13 + - Tested with nose 1.3.0 +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 14 14 + * Mock by Michael Foord used for tests +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 15 15 + - Tested with 1.0.1 +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 16 16 + * Coverage - used for tests coverage +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 17 17 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 18 18 + The list of dependencies are listed in /requirements.txt and /test-requirements.txt. The installer takes care of installing them for you though. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 19 19 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 20 20 + ### INSTALL +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 21 21 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 22 22 + [![Latest Version](https://pypip.in/version/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 23 23 + [![Supported Python Versions](https://pypip.in/py_versions/GitPython/badge.svg)](https://pypi.python.org/pypi/GitPython/) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 24 24 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 25 25 + If you have downloaded the source code: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 26 26 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 27 27 + python setup.py install +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 28 28 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 29 29 + or if you want to obtain a copy from the Pypi repository: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 30 30 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 31 31 + pip install gitpython +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 32 32 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 33 33 + Both commands will install the required package dependencies. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 34 34 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 35 35 + A distribution package can be obtained for manual installation at: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 36 36 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 37 37 + http://pypi.python.org/pypi/GitPython +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 38 38 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 39 39 + ### RUNNING TESTS +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 40 40 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 41 41 + The easiest way to run test is by using [tox](https://pypi.python.org/pypi/tox) a wrapper around virtualenv. It will take care of setting up environnements with the proper dependencies installed and execute test commands. To install it simply: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 42 42 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 43 43 + pip install tox +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 44 44 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 45 45 + Then run: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 46 46 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 47 47 + tox +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 48 48 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 49 49 + ### SOURCE +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 50 50 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 51 51 + GitPython's git repo is available on GitHub, which can be browsed at [github](https://github.com/gitpython-developers/GitPython) and cloned like that: +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 52 52 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 53 53 + git clone git://github.com/gitpython-developers/GitPython.git git-python +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 54 54 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 55 55 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 56 56 + ### INFRASTRUCTURE +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 57 57 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 58 58 + * [User Documentation](http://gitpython.readthedocs.org) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 59 59 + * [Mailing List](http://groups.google.com/group/git-python) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 60 60 + * [Issue Tracker](https://github.com/gitpython-developers/GitPython/issues) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 61 61 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 62 62 + ### LICENSE +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 63 63 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 64 64 + New BSD License. See the LICENSE file. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 65 65 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 66 66 + ### DEVELOPMENT STATUS +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 67 67 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 68 68 + [![Build Status](https://travis-ci.org/gitpython-developers/GitPython.svg?branch=0.3)](https://travis-ci.org/gitpython-developers/GitPython) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 69 69 + [![Coverage Status](https://coveralls.io/repos/gitpython-developers/GitPython/badge.png?branch=master)](https://coveralls.io/r/gitpython-developers/GitPython?branch=master) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 70 70 + [![Documentation Status](https://readthedocs.org/projects/gitpython/badge/?version=stable)](https://readthedocs.org/projects/gitpython/?badge=stable) +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 71 71 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 72 72 + Now that there seems to be a massive user base, this should be motivation enough to let git-python return to a proper state, which means +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 73 73 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 74 74 + * no open pull requests +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 75 75 + * no open issues describing bugs +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 76 76 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 77 77 + #### FUTURE GOALS +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 78 78 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 79 79 + There has been a lot of work in the master branch, which is the direction I want git-python to go. Namely, it should be able to freely mix and match the back-end used, depending on your requirements and environment. +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 80 80 + +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 81 81 + * make new master work similarly to 0.3, but with the option to swap for at least one additional backend +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 82 82 + * make a 1.0 release +e40ad6369bc74d01af4dc41d3a9b8e25ac2aa01e 83 83 + * add backends as required diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_change_in_type_raw b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_change_in_type_raw new file mode 100644 index 0000000..0793e1b --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_change_in_type_raw @@ -0,0 +1 @@ +:100644 120000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 42061c01a1c70097d1e4579f29a5adf40abdec95 T this diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_copied_mode_raw b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_copied_mode_raw new file mode 100644 index 0000000..7640f3a --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_copied_mode_raw @@ -0,0 +1 @@ +:100644 100644 cfe9deac6e10683917e80f877566b58644aa21df cfe9deac6e10683917e80f877566b58644aa21df C100 test1.txt test2.txt diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_colon b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_colon new file mode 100644 index 0000000000000000000000000000000000000000..4058b1715bd164ef8c13c73a458426bc43dcc5d0 GIT binary patch literal 351 zcmY+9L2g4a2nBN#pCG{o^G)v1GgM%p{ZiCa>X(|{zOIx_Suo3abFBbORGtVHk0xf# zt1}_luqGPY*44*sL1T85T9COlPLmCE!c-N<>|J8`qgK z10mULiQo3)5|87u=(dFaN+(aTwH5}_u&!;nb*vEUE4_8K%$Smeu+|L?+-VFY9wIF} c#^^1?Cph;RUU3PJ_&P3s@74Fr^XJd$7YhDgzW@LL literal 0 HcmV?d00001 diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_spaces b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_spaces new file mode 100644 index 0000000..a9f0b06 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_file_with_spaces @@ -0,0 +1,7 @@ +diff --git a/file with spaces b/file with spaces +new file mode 100644 +index 0000000000000000000000000000000000000000..75c620d7b0d3b0100415421a97f553c979d75174 +--- /dev/null ++++ b/file with spaces +@@ -0,0 +1 @@ ++ohai diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_index_raw b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_index_raw new file mode 100644 index 0000000..c25f380 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_index_raw @@ -0,0 +1 @@ +:100644 000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 D diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_patch_unsafe_paths b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_patch_unsafe_paths new file mode 100644 index 0000000..1aad675 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_patch_unsafe_paths @@ -0,0 +1,89 @@ +diff --git a/path/ starting with a space b/path/ starting with a space +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ b/path/ starting with a space +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/\"with-quotes\"" "b/path/\"with-quotes\"" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/\"with-quotes\"" +@@ -0,0 +1 @@ ++dummy content +diff --git a/path/'with-single-quotes' b/path/'with-single-quotes' +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ b/path/'with-single-quotes' +@@ -0,0 +1 @@ ++dummy content +diff --git a/path/ending in a space b/path/ending in a space +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ b/path/ending in a space +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/with\ttab" "b/path/with\ttab" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/with\ttab" +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/with\nnewline" "b/path/with\nnewline" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/with\nnewline" +@@ -0,0 +1 @@ ++dummy content +diff --git a/path/with spaces b/path/with spaces +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ b/path/with spaces +@@ -0,0 +1 @@ ++dummy content +diff --git a/path/with-question-mark? b/path/with-question-mark? +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ b/path/with-question-mark? +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/¯\\_(ツ)_|¯" "b/path/¯\\_(ツ)_|¯" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/¯\\_(ツ)_|¯" +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/\360\237\222\251.txt" "b/path/\360\237\222\251.txt" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/\360\237\222\251.txt" +@@ -0,0 +1 @@ ++dummy content +diff --git "a/path/\200-invalid-unicode-path.txt" "b/path/\200-invalid-unicode-path.txt" +new file mode 100644 +index 0000000000000000000000000000000000000000..eaf5f7510320b6a327fb308379de2f94d8859a54 +--- /dev/null ++++ "b/path/\200-invalid-unicode-path.txt" +@@ -0,0 +1 @@ ++dummy content +diff --git a/a/with spaces b/b/with some spaces +similarity index 100% +rename from a/with spaces +rename to b/with some spaces +diff --git a/a/ending in a space b/b/ending with space +similarity index 100% +rename from a/ending in a space +rename to b/ending with space +diff --git "a/a/\"with-quotes\"" "b/b/\"with even more quotes\"" +similarity index 100% +rename from "a/\"with-quotes\"" +rename to "b/\"with even more quotes\"" diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_raw_binary b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_raw_binary new file mode 100644 index 0000000..d4673fa --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_raw_binary @@ -0,0 +1 @@ +:100755 100755 f4567df37451b230b1381b1bc9c2bcad76e08a3c 736bd596a36924d30b480942e9475ce0d734fa0d M rps diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename new file mode 100644 index 0000000..2d5241e --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename @@ -0,0 +1,12 @@ +commit 2524c44334a8ba6b2ab8f3f0a478f04c5b073cc8 +tree e126e7b4203dadf083f5eb8e2f34c255b51d8bee +parent d789e23b9ea8d90221d13c46f7c228d729385f92 +author Michael Trier 1229389391 -0500 +committer Michael Trier 1229389391 -0500 + + Renamed AUTHORS to CONTRIBUTORS because it's cooler. + +diff --git a/AUTHORS b/CONTRIBUTORS +similarity index 100% +rename from Jérôme +rename to müller diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename_raw b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename_raw new file mode 100644 index 0000000..92d06d2 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/diff_rename_raw @@ -0,0 +1 @@ +:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 R100 this that diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/for_each_ref_with_path_component b/crates/gitgpui-git-gix/tests/fixtures/gitpython/for_each_ref_with_path_component new file mode 100644 index 0000000000000000000000000000000000000000..e723b4ae992aa1ca29d00c8cb37d646f7e3d8bda GIT binary patch literal 84 zcmWN;K@xx<2mnCOeMYe?d=3zl2XjX4|FzrIhQF20;kz2iY`r{}H>LS@1*eCw2@q=9 a62y=)Faz$E!Bz%FLH 1191997100 -0700 +committer Tom Preston-Werner 1191997100 -0700 + + initial grit setup + diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/rev_list_single b/crates/gitgpui-git-gix/tests/fixtures/gitpython/rev_list_single new file mode 100644 index 0000000..d8c6431 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/rev_list_single @@ -0,0 +1,7 @@ +commit 4c8124ffcf4039d292442eeccabdeca5af5c5017 +tree 672eca9b7f9e09c22dcb128c283e8c3c8d7697a4 +parent 634396b2f541a9f2d58b00be1a07f0c358b999b3 +author Tom Preston-Werner 1191999972 -0700 +committer Tom Preston-Werner 1191999972 -0700 + + implement Grit#heads diff --git a/crates/gitgpui-git-gix/tests/fixtures/gitpython/uncommon_branch_prefix_FETCH_HEAD b/crates/gitgpui-git-gix/tests/fixtures/gitpython/uncommon_branch_prefix_FETCH_HEAD new file mode 100644 index 0000000..7df36f2 --- /dev/null +++ b/crates/gitgpui-git-gix/tests/fixtures/gitpython/uncommon_branch_prefix_FETCH_HEAD @@ -0,0 +1,6 @@ +c2e3c20affa3e2b61a05fdc9ee3061dd416d915e 'refs/pull/1/head' of http://github.com/loic-bot/testrepo +fd8695d980e2c6df62b7785f93fd6292d1e283fb 'refs/pull/1/merge' of http://github.com/loic-bot/testrepo +bb46faf089720d1a3f9e4dc3b11ed5ff77d7e764 'refs/pull/2/head' of http://github.com/loic-bot/testrepo +5faa366d58454eceea811e0e34c502bdd7b37e4b 'refs/pull/2/merge' of http://github.com/loic-bot/testrepo +b3ad3c4f1864b50d4d3e09320947a1a3c34c9ea2 'refs/pull/3/head' of http://github.com/loic-bot/testrepo +71fe57e511776042b009ed4bb281b62b0522b434 'refs/pull/3/merge' of http://github.com/loic-bot/testrepo diff --git a/crates/gitgpui-ui-gpui/assets/icons/gitgpui_mark.svg b/crates/gitgpui-ui-gpui/assets/icons/gitgpui_mark.svg index 8c7eec7..9411e94 100644 --- a/crates/gitgpui-ui-gpui/assets/icons/gitgpui_mark.svg +++ b/crates/gitgpui-ui-gpui/assets/icons/gitgpui_mark.svg @@ -1,12 +1,3 @@ - - - - - - + + + \ No newline at end of file diff --git a/crates/gitgpui-ui-gpui/src/assets.rs b/crates/gitgpui-ui-gpui/src/assets.rs index 5547745..00d453f 100644 --- a/crates/gitgpui-ui-gpui/src/assets.rs +++ b/crates/gitgpui-ui-gpui/src/assets.rs @@ -6,11 +6,11 @@ pub struct GitGpuiAssets; impl GitGpuiAssets { fn load_static(path: &str) -> Option> { match path { - "gitgpui_logo.svg" => Some(Cow::Borrowed(include_bytes!( - "../../../assets/gitgpui_logo.svg" + "gitcomet-512.svg" => Some(Cow::Borrowed(include_bytes!( + "../../../assets/gitcomet-512.svg" ))), - "gitgpui_logo_window.svg" => Some(Cow::Borrowed(include_bytes!( - "../../../assets/gitgpui_logo_window.svg" + "gitcomet_logo_window.svg" => Some(Cow::Borrowed(include_bytes!( + "../../../assets/gitcomet_logo_window.svg" ))), "icons/arrow_down.svg" => Some(Cow::Borrowed(include_bytes!( "../assets/icons/arrow_down.svg" @@ -62,8 +62,8 @@ impl GitGpuiAssets { fn list_static(dir: &str) -> Vec { match dir.trim_end_matches('/') { "" => vec![ - "gitgpui_logo.svg".into(), - "gitgpui_logo_window.svg".into(), + "gitcomet-512.svg".into(), + "gitcomet_logo_window.svg".into(), "icons".into(), ], "icons" => vec![ diff --git a/crates/gitgpui-ui-gpui/src/view/chrome.rs b/crates/gitgpui-ui-gpui/src/view/chrome.rs index 3b1e1b4..4b2b4b5 100644 --- a/crates/gitgpui-ui-gpui/src/view/chrome.rs +++ b/crates/gitgpui-ui-gpui/src/view/chrome.rs @@ -34,13 +34,9 @@ fn titlebar_app_icon(theme: AppTheme) -> AnyElement { .id("titlebar_app_icon") .size(px(16.0)) .rounded(px(4.0)) - .bg(with_alpha( - theme.colors.text, - if theme.is_dark { 0.12 } else { 0.08 }, - )) .overflow_hidden() .child( - gpui::img("gitgpui_logo_window.svg") + gpui::img("gitcomet_logo_window.svg") .size(px(16.0)) .object_fit(ObjectFit::Contain) .with_fallback(move || { diff --git a/crates/gitgpui-ui-gpui/src/view/linux_desktop_integration.rs b/crates/gitgpui-ui-gpui/src/view/linux_desktop_integration.rs index 60dcca9..f54f4ec 100644 --- a/crates/gitgpui-ui-gpui/src/view/linux_desktop_integration.rs +++ b/crates/gitgpui-ui-gpui/src/view/linux_desktop_integration.rs @@ -31,7 +31,7 @@ impl GitGpuiView { }; let desktop_path = data_home.join("applications/gitgpui.desktop"); - let icon_path = data_home.join("icons/hicolor/scalable/apps/gitgpui.svg"); + let icon_path = data_home.join("icons/hicolor/scalable/apps/gitcomet-512.svg"); if desktop_path.exists() && icon_path.exists() { return; } @@ -58,7 +58,7 @@ impl GitGpuiView { )); const ICON_SVG: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../assets/gitgpui_logo.svg" + "/../../assets/gitcomet-512.svg" )); let exe = std::env::current_exe().map_err(|_| { @@ -76,7 +76,7 @@ impl GitGpuiView { let applications_dir = data_home.join("applications"); let icons_dir = data_home.join("icons/hicolor/scalable/apps"); let desktop_path = applications_dir.join("gitgpui.desktop"); - let icon_path = icons_dir.join("gitgpui.svg"); + let icon_path = icons_dir.join("gitcomet-512.svg"); fs::create_dir_all(&applications_dir) .and_then(|_| fs::create_dir_all(&icons_dir)) diff --git a/scripts/install-linux.sh b/scripts/install-linux.sh index 73bbb07..6dfb032 100755 --- a/scripts/install-linux.sh +++ b/scripts/install-linux.sh @@ -8,7 +8,7 @@ Usage: scripts/install-linux.sh [--release|--debug] [--prefix PATH] [--no-build] Installs: - binary to /bin/gitgpui-app - desktop entry to ~/.local/share/applications/gitgpui.desktop - - icon to ~/.local/share/icons/hicolor/scalable/apps/gitgpui.svg + - icon to ~/.local/share/icons/hicolor/scalable/apps/gitcomet-512.svg Defaults: --release, --prefix ~/.local, build if needed @@ -56,8 +56,8 @@ sed "s|^Exec=.*$|Exec=${bindir}/gitgpui-app|g" \ "${repo_root}/assets/linux/gitgpui.desktop" >"$tmp_desktop" install -Dm644 "$tmp_desktop" "${appdir}/gitgpui.desktop" -install -Dm644 "${repo_root}/assets/gitgpui_logo.svg" \ - "${icondir}/gitgpui.svg" +install -Dm644 "${repo_root}/assets/gitcomet-512.svg" \ + "${icondir}/gitcomet-512.svg" command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$appdir" >/dev/null 2>&1 || true command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache "${XDG_DATA_HOME:-${HOME}/.local/share}/icons/hicolor" >/dev/null 2>&1 || true @@ -65,5 +65,5 @@ command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache "${XDG echo "Installed GitGpui:" echo " ${bindir}/gitgpui-app" echo " ${appdir}/gitgpui.desktop" -echo " ${icondir}/gitgpui.svg" +echo " ${icondir}/gitcomet-512.svg" echo "If GNOME still shows a generic icon, log out/in (or restart GNOME Shell)." diff --git a/scripts/uninstall-linux.sh b/scripts/uninstall-linux.sh index 0e2616f..8e50f85 100755 --- a/scripts/uninstall-linux.sh +++ b/scripts/uninstall-linux.sh @@ -19,7 +19,7 @@ icondir="${XDG_DATA_HOME:-${HOME}/.local/share}/icons/hicolor/scalable/apps" rm -f "${bindir}/gitgpui-app" rm -f "${appdir}/gitgpui.desktop" -rm -f "${icondir}/gitgpui.svg" +rm -f "${icondir}/gitcomet-512.svg" command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$appdir" >/dev/null 2>&1 || true command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache "${XDG_DATA_HOME:-${HOME}/.local/share}/icons/hicolor" >/dev/null 2>&1 || true