From de1e81aa10e02f286f39c73cda9899908c448790 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Thu, 15 Nov 2012 09:08:30 -0800 Subject: [PATCH 01/11] oidmap: Enhance the khash wrapper --- src/oidmap.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/oidmap.h b/src/oidmap.h index a29c7cd35..50da54b1c 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -32,4 +32,17 @@ GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) #define git_oidmap_alloc() kh_init(oid) #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL +#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k) +#define git_oidmap_valid_index(h, idx) (idx != kh_end(h)) + +#define git_oidmap_value_at(h, idx) kh_val(h, idx) + +#define git_oidmap_insert(h, key, val, rval) do { \ + khiter_t __pos = kh_put(oid, h, key, &rval); \ + if (rval >= 0) { \ + if (rval == 0) kh_key(h, __pos) = key; \ + kh_val(h, __pos) = val; \ + } } while (0) + +#define git_oidmap_foreach_value kh_foreach_value #endif From 4cc71bb7fb7d10347bfa8ba57cb4cf74dcc79d4b Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 13 Nov 2012 11:48:07 +0100 Subject: [PATCH 02/11] tests: Add "describe" test repository Built with the following script: #!/bin/sh test_tick () { sleep 10 } test_tick && echo one >file && git add file && git commit -m initial && one=$(git rev-parse HEAD) && git describe --always HEAD && test_tick && echo two >file && git add file && git commit -m second && two=$(git rev-parse HEAD) && test_tick && echo three >file && git add file && git commit -m third && test_tick && echo A >file && git add file && git commit -m A && test_tick && git tag -a -m A A && test_tick && echo c >file && git add file && git commit -m c && test_tick && git tag c && git reset --hard $two && test_tick && echo B >side && git add side && git commit -m B && test_tick && git tag -a -m B B && test_tick && git merge -m Merged c && merged=$(git rev-parse HEAD) && git reset --hard $two && test_tick && echo D >another && git add another && git commit -m D && test_tick && git tag -a -m D D && test_tick && git tag -a -m R R && test_tick && echo DD >another && git commit -a -m another && test_tick && git tag e && test_tick && echo DDD >another && git commit -a -m "yet another" && test_tick && git merge -m Merged $merged && test_tick && echo X >file && echo X >side && git add file side && git commit -m x --- tests/resources/describe/.gitted/HEAD | Bin 0 -> 24 bytes tests/resources/describe/.gitted/config | Bin 0 -> 165 bytes tests/resources/describe/.gitted/index | Bin 0 -> 262 bytes tests/resources/describe/.gitted/logs/HEAD | Bin 0 -> 2298 bytes .../describe/.gitted/logs/refs/heads/master | Bin 0 -> 2298 bytes .../03/00021985931292d0611b9232e757035fefc04d | Bin 0 -> 108 bytes .../10/8b485d8268ea595df8ffea74f0f4b186577d32 | Bin 0 -> 125 bytes .../10/bd08b099ecb79184c60183f5c94ca915f427ad | Bin 0 -> 127 bytes .../17/8481050188cf00d7d9cd5a11e43ab8fab9294f | Bin 0 -> 17 bytes .../19/1faf88a5826a99f475baaf8b13652c4e40bfe6 | Bin 0 -> 19 bytes .../1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf | Bin 0 -> 156 bytes .../22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 | Bin 0 -> 17 bytes .../25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 | Bin 0 -> 87 bytes .../2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 | Bin 0 -> 21 bytes .../31/fc9136820b507e938a9c6b88bf2c567a9f6f4b | Bin 0 -> 153 bytes .../42/8f9554a2eec22de29898819b579466af7c1583 | Bin 0 -> 80 bytes .../4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f | Bin 0 -> 155 bytes .../4f/2d9ce01ad5249cabdc6565366af8aff85b1525 | Bin 0 -> 18 bytes .../52/912fbab0715dec53d43053966e78ad213ba359 | Bin 0 -> 127 bytes .../56/26abf0f72e58d7a153368ba57db4c673c0e171 | Bin 0 -> 19 bytes .../61/26a5f9c57ebc81e64370ec3095184ad92dab1c | Bin 0 -> 151 bytes .../62/d8fe9f6db631bd3a19140699101c9e281c9f9d | Bin 0 -> 17 bytes .../65/a91bc2262480dce4c5979519aae6668368eb4e | Bin 0 -> 77 bytes .../68/0166b6cd31f76354fee2572618e6b0142d05e6 | Bin 0 -> 127 bytes .../69/3a3de402bb23897ed5c931273e53c78eff0495 | Bin 0 -> 49 bytes .../6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 | Bin 0 -> 154 bytes .../6d/218e42592043041c4da016ff298cf241b86c3c | Bin 0 -> 77 bytes .../75/bb152c600647586c226d98411b1d2f9861af5a | Bin 0 -> 80 bytes .../81/f4b1aac643e6983fab370eae8aefccecbf3a4c | Bin 0 -> 152 bytes .../8e/c1d96451ff05451720e4e8968812c46b35e5e4 | Bin 0 -> 49 bytes .../94/9b98e208015bfc0e2f573debc34ae2f97a7f0e | Bin 0 -> 186 bytes .../9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 | Bin 0 -> 49 bytes .../a6/095f816e81f64651595d488badc42399837d6a | Bin 0 -> 153 bytes .../a9/e3325a07117aa5381e044a8d96c26eb30d729d | Bin 0 -> 49 bytes .../a9/eb02af13df030159e39f70330d5c8a47655691 | Bin 0 -> 160 bytes .../aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 | Bin 0 -> 49 bytes .../aa/ddd4f14847e0e323924ec262c2343249a84f8b | Bin 0 -> 125 bytes .../b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 | Bin 0 -> 155 bytes .../ce/1c4f8b6120122e23d4442925d98c56c41917d8 | Bin 0 -> 187 bytes .../d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 | Bin 0 -> 80 bytes .../f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 | Bin 0 -> 17 bytes .../f7/0f10e4db19068f79bc43844b49f3eece45c4e8 | Bin 0 -> 17 bytes .../f7/19efd430d52bcfc8566a43b2eb655688d38871 | Bin 0 -> 19 bytes .../resources/describe/.gitted/refs/heads/master | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/A | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/B | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/D | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/R | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/c | Bin 0 -> 42 bytes tests/resources/describe/.gitted/refs/tags/e | Bin 0 -> 42 bytes tests/resources/describe/another | Bin 0 -> 5 bytes tests/resources/describe/file | Bin 0 -> 3 bytes tests/resources/describe/side | Bin 0 -> 3 bytes 53 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/resources/describe/.gitted/HEAD create mode 100644 tests/resources/describe/.gitted/config create mode 100644 tests/resources/describe/.gitted/index create mode 100644 tests/resources/describe/.gitted/logs/HEAD create mode 100644 tests/resources/describe/.gitted/logs/refs/heads/master create mode 100644 tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d create mode 100644 tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 create mode 100644 tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad create mode 100644 tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f create mode 100644 tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 create mode 100644 tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf create mode 100644 tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 create mode 100644 tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 create mode 100644 tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 create mode 100644 tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b create mode 100644 tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 create mode 100644 tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f create mode 100644 tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 create mode 100644 tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 create mode 100644 tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 create mode 100644 tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c create mode 100644 tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d create mode 100644 tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e create mode 100644 tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 create mode 100644 tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495 create mode 100644 tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 create mode 100644 tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c create mode 100644 tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a create mode 100644 tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c create mode 100644 tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 create mode 100644 tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e create mode 100644 tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 create mode 100644 tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a create mode 100644 tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d create mode 100644 tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691 create mode 100644 tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 create mode 100644 tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b create mode 100644 tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 create mode 100644 tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 create mode 100644 tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 create mode 100644 tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 create mode 100644 tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 create mode 100644 tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 create mode 100644 tests/resources/describe/.gitted/refs/heads/master create mode 100644 tests/resources/describe/.gitted/refs/tags/A create mode 100644 tests/resources/describe/.gitted/refs/tags/B create mode 100644 tests/resources/describe/.gitted/refs/tags/D create mode 100644 tests/resources/describe/.gitted/refs/tags/R create mode 100644 tests/resources/describe/.gitted/refs/tags/c create mode 100644 tests/resources/describe/.gitted/refs/tags/e create mode 100644 tests/resources/describe/another create mode 100644 tests/resources/describe/file create mode 100644 tests/resources/describe/side diff --git a/tests/resources/describe/.gitted/HEAD b/tests/resources/describe/.gitted/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb4380516445d96678ebb53747e38764a1f568ba GIT binary patch literal 24 fcmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;^hJWYzYWX literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/config b/tests/resources/describe/.gitted/config new file mode 100644 index 0000000000000000000000000000000000000000..454e576b97eddaa5bb9ccbc4df5365ce556b6039 GIT binary patch literal 165 zcmYj~K@P$&3L2<9KEMsZkq00Srg4ge6IZrdDZj^&K;pQ&v%B|M6d&VoP+Y~x zMOzjv;5(|3)af)GBhAEEBw@%5Y#7!P^k1?pz^s_JHG!iA>AH2$T4Ad1?)q`N^h0yHi`uo+k+<=1yCZOWSYrF7T&ZsAlNKZpI# z7}yi@@=G#Oi+~D)T!KD=)C0|#kE9RCL@|dc>BhhLx!Vl)T1kqq%@mNCry(iZfH7=7a>fx&n=1W6(8JFyLCW&gbA7u3wwPAIE$=b}`&IIf7XL E0L~UVAOHXW literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/logs/HEAD b/tests/resources/describe/.gitted/logs/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..fc49c6fa362794e5ba5e2fc490a3683c27f24bcc GIT binary patch literal 2298 zcmchY%Z?L43`N&O%0JX@Ku9Ck!}W*|0>qY&aJlSEi#()G&xrYZ3YZ{GR@E%Xs?wW# zY+qjkjQxy-Ttmi|UCcsQuxVBmN;7$K}oEGzTI!((&hj%;k^4e~Og+q&oxn`vlLnW!8wy>3q zscEqS(g+E34Pk)SggbK)KA+llGptr!00yl&Gg5R#0Hm^Y36-pRFFpoJfUB_VE!Ofd z&7EpkEZbj0BjOmm#FD*I^$CO|lPI%rRZoFeQ8|1y&Yk(Z-3$vw>ms2R#?Wz;g*QEn_;2h5wEa(z$?)#7g=`Zcz&8rd$XL)sN4$gd8=HkkJ&** zNK$;J!m)d?s$-0aipFk)Ra1`F3jQqZ&V1Q!hJ{!x2hFt)0jzW4dM?6^F7c|wMwf0vh)(BjfVp3iQ8_C8csNq)n}P?6MSRf&-a|vq6jHx5v8?J8 zhFh*@SqY&aJlSEi#()G&xrYZ3YZ{GR@E%Xs?wW# zY+qjkjQxy-Ttmi|UCcsQuxVBmN;7$K}oEGzTI!((&hj%;k^4e~Og+q&oxn`vlLnW!8wy>3q zscEqS(g+E34Pk)SggbK)KA+llGptr!00yl&Gg5R#0Hm^Y36-pRFFpoJfUB_VE!Ofd z&7EpkEZbj0BjOmm#FD*I^$CO|lPI%rRZoFeQ8|1y&Yk(Z-3$vw>ms2R#?Wz;g*QEn_;2h5wEa(z$?)#7g=`Zcz&8rd$XL)sN4$gd8=HkkJ&** zNK$;J!m)d?s$-0aipFk)Ra1`F3jQqZ&V1Q!hJ{!x2hFt)0jzW4dM?6^F7c|wMwf0vh)(BjfVp3iQ8_C8csNq)n}P?6MSRf&-a|vq6jHx5v8?J8 zhFh*@S$`e7{s)WtG!-Jcit9d0@+sKc`{x<@8wQ5SgQ&99K3sSr z%3Fj42U=zJ-9U&Q50@io;{hFHAFvcDJ{4WGu<71(eUS-{^ZD@?en)^3r=-)6fFx+ZghXx3j z(M9wOsi5~Wfd~UE0+=u*i`hq$`L9=4r^xp9_^Q&P>5B4*&`gCZnWhCk_Ar literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 new file mode 100644 index 0000000000000000000000000000000000000000..d0398e6e35843bd981cec5cab597cfe98a20641d GIT binary patch literal 21 ccmblwFt{&6XD~D{Ff%bxNX*MG$w)0?@YkL5KoYi+yQ_Zq|tp5=$ mstQ$-mYI{v@Llr#6@#nV=TC%XId6KM8rE^SqYwZE+8%DJ{4W%rafMQPm0exi(5S_j`!{Fk-!L#Rsn@zrX^g+j z!7LFoWS_X$s6A2uvc?1?L`*T#VlqW0@5C1ew<)y(a^N$V1sHwwxj-q5!4*{;X;^#+ z*&2bzeD@*Sv2C+|xXC+L9&5T49u@cJtKz!dQm+rGEhwX+Q%+t_P~x=bN3{w7^S@F}(?p2SK4UhzoBoZYHyt87_g4cxnDg zVSo}bpitUM?l*1-m30`jx7J4)HfW8&&E# hERlM3??DJ{4WG`H$I@0US-{^ZD@?en)^3r=-)6fFx+Zghjz|f zMiaq_VZNY4b*dvHf5ej7pMo1z_VHVB> z2wBlFUajl>*tXH$vg?~v=_^k~dZq8 FM}&2eM&$qi literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d new file mode 100644 index 0000000000000000000000000000000000000000..734f7dc4297b667e4acfca190dffaab6fc015706 GIT binary patch literal 17 Ycmb6VK6i>Ff%bxNXyJgW%w@n{))j>?eizXvYa=)P7Uk0+))UX jFV0L!Wl*xNF#9d}_nY^k%V!gdk1c8zk*@#%-#Q&|6`CU{ literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 b/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 new file mode 100644 index 0000000000000000000000000000000000000000..36f198686139bb9c14e3e6af046ddfc6043c1d30 GIT binary patch literal 127 zcmV-_0D%8^0Tql(4#FT1KwWc+*_$*2q~>-Pq*Wvp8HWVTzGp0iBLP4#FT106p^x?k3GH!WI%^Jou3X7HurWApO66gSW{flT2#0E<+mw zr_n|9>|#V`5&@8%ou_0B<=`!1#B56Ngj^h}4|myywwavs(HEY{MwSQ#LqhRx&LyCL zWOfWi@jhOy>uq1x(cZG@t5oSrnu_$w-}|%jvQDYho5qU=1HlH;=L0|;_Lv;6VK6i>Ff%bxNXyJgW%#r6XD~D{Ff%bxNX*MG$w)0?5N~N@W$ZZ5aQ)`lD8VOIJAUod m^oJ@*%gjk-_%8YWiosRw^C!ZxoHxBr4ePkvQ3wF)fgTu2AG0y74UACbUTF6jR2-)$3Q2-=mgWeT3g-B?! z?7=DC$E$U{?dv+)TQ+@_Dt*b9BE9nW{;a&Lm)z=28w!9BEjoQZ0MucR$svE{>Y;v6 G&_z7PhD&Y$ literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 new file mode 100644 index 0000000000000000000000000000000000000000..432b2c1934abe84c9e4a2b706ed5f51c26fe78d6 GIT binary patch literal 49 zcmV-10M7q-0V^p=O;s>9VlXr?Ff%bxNXyJgWzfE#zItQwlK-2ofBw+HGyf@D&+{e# HAyW}|D3l0DXC9yapp$Y+U6LO@HBRFo_3ixWi^U!nJEV=>ZhOV~dB8xg{3v^Jq zr0ip04uXGtQ7pctEg%6BDY@#sUqf6a6%NItV64gH=lr~eJ)g%huP;6AUAvCG+?tM_ of8HNG@5ilN{mr6FKp@P~zODdfwd>T>f9mE#$3v_61<>D8(bq6rssI20 literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 new file mode 100644 index 0000000000000000000000000000000000000000..5fff6fa1f09472bebd210fa4595edd2df3cf3454 GIT binary patch literal 49 zcmb}1kzOc(H3fdZ{j8^z_e-vJ~Db{k6%6QTg?8V9R7$k-rCp-Sk#aH|Q Hxid$IwD?Oi literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d new file mode 100644 index 0000000000000000000000000000000000000000..ee45b76500979b29c85a78bd0253fc7f96421ebe GIT binary patch literal 49 zcmV-10M7q-0V^p=O;s>9VlXr?Ff%bxNXyJgW%$l7@Z`26TYu#q=N50z&+pE;9(e%( H1x63p%}x|j literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691 b/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691 new file mode 100644 index 0000000000000000000000000000000000000000..320161a558930a252785c79391c639da66602a4b GIT binary patch literal 160 zcmV;R0AK%j0iBLP3d0~21-teX?h8TvCNWS-ukzJr6B=W&X3^W%Gj#WuVc><5^VXyx zb!iolab)&JFYL69(ZhOTMofxA1lvV-=@6`VbH&l5hSHNIjWBy-Vk8(!gei?_a*0Nl z7Gvg+1fOj!RUYTQxAMXvZ^)=y7!qpc&*PbS+lNr{U21a5sAaUXy!N3)r~PEZpSk#= ONj^$jQN<5`XiFtrx=)M% literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 new file mode 100644 index 0000000000000000000000000000000000000000..4cbaff19220b3f9325a2e9c3135ce500d28619f0 GIT binary patch literal 49 zcmV-10M7q-0V^p=O;s>9VlXr?Ff%bxNXyJgW%w@n{))j>?eizXvYa=)P7Uk0+))Ss H8ORWB*xVH2 literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b new file mode 100644 index 0000000000000000000000000000000000000000..651ec782e79bd0b03a186e6d82a86985c20840a5 GIT binary patch literal 125 zcmV-@0D}K`0Tqlv4#FT106p`Hy_+;pu+qdB`;=v~5EM#->%rfPm&sga1QVEfJ+<&; z67VLvU=V3>mY3Drh{0YpBYWgX#1x`MM@qg21GQSSC>f9K&%_<}Pbtz~yu*zv_e@jd fUh(}%6?2(JatC9*p7rYN1JOw-< literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 b/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 new file mode 100644 index 0000000000000000000000000000000000000000..fe86e7c7c581f0b342374fec385dd99e6e305ae3 GIT binary patch literal 155 zcmV;M0A&Ao0iBLb3d0~61-teX?h8T9r%@=SSMlfBN@Fb6+`pcot2Yb`yx3|jgDXXs z(E)A|WpakJ%vtC;n-U;cXRMVfk_oGZ2CCh=mv%hY384_z{NVstgSq}g-HLY298 zlt`)MPCe8=p8%=Eo+T!&!k`pRIcIa|ovai>0=AzfBrT@&$ht7vK^9xC5RMDw4T?GID literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 new file mode 100644 index 0000000000000000000000000000000000000000..4512d16d665e06dc8a28ad9df83330f83bc3db75 GIT binary patch literal 80 zcmV-W0I&ae0V^p=O;s>6XD~D{Ff%bxNX*MG$w)0?kd$BFv9u{`=9ki4>$`KFgG+!Nli92PBAeyFg3O`GfOqMNK7#@G)_u1HnrsC0s!um3Zei2 literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/refs/tags/D b/tests/resources/describe/.gitted/refs/tags/D new file mode 100644 index 0000000000000000000000000000000000000000..90f420854e95bcdd9948a9df4e5134abc9d818ae GIT binary patch literal 42 xcmXpsNJ=rVNHVarOifNQw=}dcNj5Vuv@lLHO|~>iPP8;MO*1hvPfX$E0s!!43f%wz literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/refs/tags/R b/tests/resources/describe/.gitted/refs/tags/R new file mode 100644 index 0000000000000000000000000000000000000000..ef04b7c9f3ee17931150694f3f76650cf6a75a21 GIT binary patch literal 42 vcmV~$Nf7`r2n4WyXX=G>K@SD|OK5^51`4m0H=u4iBOU_IK@poY`QV-p0s!$F3kU!J literal 0 HcmV?d00001 diff --git a/tests/resources/describe/.gitted/refs/tags/e b/tests/resources/describe/.gitted/refs/tags/e new file mode 100644 index 0000000000000000000000000000000000000000..5e88d6f13a5693bc2d61a49001eef4a7742bc79e GIT binary patch literal 42 wcmV~$!4Uu;2m`RcckUvsBCaFS{YNmd8s?K$xi9b@3lz(>9apC0x00UV7 Date: Tue, 13 Nov 2012 16:35:24 +0100 Subject: [PATCH 03/11] object: introduce git_describe_object() --- include/git2.h | 1 + include/git2/common.h | 2 + include/git2/describe.h | 66 +++ include/git2/errors.h | 1 + src/commit_list.h | 2 +- src/describe.c | 690 ++++++++++++++++++++++++++++++ src/oidmap.h | 3 + tests/describe/describe.c | 47 ++ tests/describe/describe_helpers.c | 24 ++ tests/describe/describe_helpers.h | 9 + tests/describe/t6120.c | 135 ++++++ 11 files changed, 979 insertions(+), 1 deletion(-) create mode 100644 include/git2/describe.h create mode 100644 src/describe.c create mode 100644 tests/describe/describe.c create mode 100644 tests/describe/describe_helpers.c create mode 100644 tests/describe/describe_helpers.h create mode 100644 tests/describe/t6120.c diff --git a/include/git2.h b/include/git2.h index f74976061..6713b4961 100644 --- a/include/git2.h +++ b/include/git2.h @@ -19,6 +19,7 @@ #include "git2/commit.h" #include "git2/common.h" #include "git2/config.h" +#include "git2/describe.h" #include "git2/diff.h" #include "git2/errors.h" #include "git2/filter.h" diff --git a/include/git2/common.h b/include/git2/common.h index 32237efed..ceb27205a 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -83,6 +83,8 @@ GIT_BEGIN_DECL */ #define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000" +#define FLAG_BITS 27 + /** * Return the version of the libgit2 library * being currently used. diff --git a/include/git2/describe.h b/include/git2/describe.h new file mode 100644 index 000000000..8a00d258a --- /dev/null +++ b/include/git2/describe.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_describe_h__ +#define INCLUDE_git_describe_h__ + +#include "common.h" +#include "types.h" +#include "buffer.h" + +/** + * @file git2/describe.h + * @brief Git describing routines + * @defgroup git_describe Git describing routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +typedef enum { + GIT_DESCRIBE_DEFAULT, + GIT_DESCRIBE_TAGS, + GIT_DESCRIBE_ALL, +} git_describe_strategy_t; + +/** + * Describe options structure + * + * Zero out for defaults. Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to + * correctly set the `version` field. E.g. + * + * git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + */ +typedef struct git_describe_opts { + unsigned int version; + + unsigned int max_candidates_tags; /** default: 10 */ + unsigned int abbreviated_size; + unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */ + const char *pattern; + int always_use_long_format; + int only_follow_first_parent; + int show_commit_oid_as_fallback; +} git_describe_opts; + +#define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10 +#define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7 + +#define GIT_DESCRIBE_OPTIONS_VERSION 1 +#define GIT_DESCRIBE_OPTIONS_INIT { \ + GIT_DESCRIBE_OPTIONS_VERSION, \ + GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ + GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE} + +GIT_EXTERN(int) git_describe_object( + git_buf *out, + git_object *committish, + git_describe_opts *opts); + +/** @} */ +GIT_END_DECL + +#endif diff --git a/include/git2/errors.h b/include/git2/errors.h index e22f0d86d..287498423 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -87,6 +87,7 @@ typedef enum { GITERR_REVERT, GITERR_CALLBACK, GITERR_CHERRYPICK, + GITERR_DESCRIBE, } git_error_t; /** diff --git a/src/commit_list.h b/src/commit_list.h index 490d841be..7cd3945ae 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -25,7 +25,7 @@ typedef struct git_commit_list_node { uninteresting:1, topo_delay:1, parsed:1, - flags : 4; + flags : FLAG_BITS; unsigned short in_degree; unsigned short out_degree; diff --git a/src/describe.c b/src/describe.c new file mode 100644 index 000000000..b503db761 --- /dev/null +++ b/src/describe.c @@ -0,0 +1,690 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "git2/describe.h" + +#include "common.h" +#include "commit.h" +#include "commit_list.h" +#include "oidmap.h" +#include "refs.h" +#include "revwalk.h" +#include "tag.h" +#include "vector.h" + +/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ + +struct commit_name { + git_tag *tag; + unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ + unsigned name_checked:1; + git_oid sha1; + char *path; + + /* Khash workaround. They original key has to still be reachable */ + git_oid peeled; +}; + +static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key) +{ + khint_t pos = git_oidmap_lookup_index(map, key); + + if (!git_oidmap_valid_index(map, pos)) + return NULL; + + return git_oidmap_value_at(map, pos); +} + +static struct commit_name *find_commit_name( + git_oidmap *names, + const git_oid *peeled) +{ + return (struct commit_name *)(oidmap_value_bykey(names, peeled)); +} + +static int replace_name( + git_tag **tag, + git_repository *repo, + struct commit_name *e, + unsigned int prio, + const git_oid *sha1) +{ + git_time_t e_time = 0, t_time = 0; + + if (!e || e->prio < prio) + return 1; + + if (e->prio == 2 && prio == 2) { + /* Multiple annotated tags point to the same commit. + * Select one to keep based upon their tagger date. + */ + git_tag *t = NULL; + + if (!e->tag) { + if (git_tag_lookup(&t, repo, &e->sha1) < 0) + return 1; + e->tag = t; + } + + if (git_tag_lookup(&t, repo, sha1) < 0) + return 0; + + *tag = t; + + if (e->tag->tagger) + e_time = e->tag->tagger->when.time; + + if (t->tagger) + t_time = t->tagger->when.time; + + if (e_time < t_time) + return 1; + } + + return 0; +} + +static int add_to_known_names( + git_repository *repo, + git_oidmap *names, + const char *path, + const git_oid *peeled, + unsigned int prio, + const git_oid *sha1) +{ + struct commit_name *e = find_commit_name(names, peeled); + bool found = (e != NULL); + + git_tag *tag = NULL; + if (replace_name(&tag, repo, e, prio, sha1)) { + if (!found) { + e = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(e); + + e->path = NULL; + e->tag = NULL; + } + + if (e->tag) + git_tag_free(e->tag); + e->tag = tag; + e->prio = prio; + e->name_checked = 0; + git_oid_cpy(&e->sha1, sha1); + git__free(e->path); + e->path = git__strdup(path); + git_oid_cpy(&e->peeled, peeled); + + if (!found) { + int ret; + + git_oidmap_insert(names, &e->peeled, e, ret); + if (ret < 0) + return -1; + } + } + else + git_tag_free(tag); + + return 0; +} + +static int retrieve_peeled_tag_or_object_oid( + git_oid *peeled_out, + git_oid *ref_target_out, + git_repository *repo, + const char *refname) +{ + git_reference *ref; + git_object *peeled = NULL; + int error; + + if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0) + return error; + + if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0) + goto cleanup; + + git_oid_cpy(ref_target_out, git_reference_target(ref)); + git_oid_cpy(peeled_out, git_object_id(peeled)); + + if (git_oid_cmp(ref_target_out, peeled_out) != 0) + error = 1; /* The reference was pointing to a annotated tag */ + else + error = 0; /* Any other object */ + +cleanup: + git_reference_free(ref); + git_object_free(peeled); + return error; +} + +struct get_name_data +{ + git_describe_opts *opts; + git_repository *repo; + git_oidmap *names; +}; + +static int get_name(const char *refname, void *payload) +{ + struct get_name_data *data; + bool is_tag, is_annotated, all; + git_oid peeled, sha1; + unsigned int prio; + int error = 0; + + data = (struct get_name_data *)payload; + is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR); + all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; + + /* Reject anything outside refs/tags/ unless --all */ + if (!all && !is_tag) + return 0; + + /* Accept only tags that match the pattern, if given */ + if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern, + refname + strlen(GIT_REFS_TAGS_DIR), 0))) + return 0; + + /* Is it annotated? */ + if ((error = retrieve_peeled_tag_or_object_oid( + &peeled, &sha1, data->repo, refname)) < 0) + return error; + + is_annotated = error; + + /* + * By default, we only use annotated tags, but with --tags + * we fall back to lightweight ones (even without --tags, + * we still remember lightweight ones, only to give hints + * in an error message). --all allows any refs to be used. + */ + if (is_annotated) + prio = 2; + else if (is_tag) + prio = 1; + else + prio = 0; + + add_to_known_names(data->repo, data->names, + all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR), + &peeled, prio, &sha1); + return 0; +} + +struct possible_tag { + struct commit_name *name; + int depth; + int found_order; + unsigned flag_within; +}; + +static int compare_pt(const void *a_, const void *b_) +{ + struct possible_tag *a = (struct possible_tag *)a_; + struct possible_tag *b = (struct possible_tag *)b_; + if (a->depth != b->depth) + return a->depth - b->depth; + if (a->found_order != b->found_order) + return a->found_order - b->found_order; + return 0; +} + +#define SEEN (1u << 0) + +static unsigned long finish_depth_computation( + git_pqueue *list, + git_revwalk *walk, + struct possible_tag *best) +{ + unsigned long seen_commits = 0; + int error, i; + + while (git_pqueue_size(list) > 0) { + git_commit_list_node *c = git_pqueue_pop(list); + seen_commits++; + if (c->flags & best->flag_within) { + size_t index = 0; + while (git_pqueue_size(list) > index) { + git_commit_list_node *i = git_pqueue_get(list, index); + if (!(i->flags & best->flag_within)) + break; + index++; + } + if (index > git_pqueue_size(list)) + break; + } else + best->depth++; + for (i = 0; i < c->out_degree; i++) { + git_commit_list_node *p = c->parents[i]; + if ((error = git_commit_list_parse(walk, p)) < 0) + return error; + if (!(p->flags & SEEN)) + if ((error = git_pqueue_insert(list, p)) < 0) + return error; + p->flags |= c->flags; + } + } + return seen_commits; +} + +static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n) +{ + if (n->prio == 2 && !n->tag) { + if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) { + giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path); + return -1; + } + } + + if (n->tag && !n->name_checked) { + if (!git_tag_name(n->tag)) { + giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path); + return -1; + } + + /* TODO: Cope with warnings + if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) + warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path); + */ + + n->name_checked = 1; + } + + if (n->tag) + git_buf_printf(buf, "%s", git_tag_name(n->tag)); + else + git_buf_printf(buf, "%s", n->path); + + return 0; +} + +static int find_unique_abbrev_size( + int *out, + const git_oid *oid_in, + int abbreviated_size) +{ + GIT_UNUSED(abbreviated_size); + + /* TODO: Actually find the abbreviated oid size */ + GIT_UNUSED(oid_in); + + *out = GIT_OID_HEXSZ; + + return 0; +} + +static int show_suffix( + git_buf *buf, + int depth, + const git_oid* id, + size_t abbrev_size) +{ + int error, size; + + char hex_oid[GIT_OID_HEXSZ]; + + if ((error = find_unique_abbrev_size(&size, id, abbrev_size)) < 0) + return error; + + git_oid_fmt(hex_oid, id); + + git_buf_printf(buf, "-%d-g", depth); + + git_buf_put(buf, hex_oid, size); + git_buf_putc(buf, '\0'); + + return git_buf_oom(buf) ? -1 : 0; +} + +#define MAX_CANDIDATES_TAGS FLAG_BITS - 1 + +static int describe_not_found(const git_oid *oid, const char *message_format) { + char oid_str[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oid_str, sizeof(oid_str), oid); + + giterr_set(GITERR_DESCRIBE, message_format, oid_str); + return GIT_ENOTFOUND; +} + +static int describe( + git_buf *out, + struct get_name_data *data, + git_commit *commit, + const char *dirty_suffix) +{ + struct commit_name *n; + struct possible_tag *best; + bool all, tags; + git_buf buf = GIT_BUF_INIT; + git_revwalk *walk = NULL; + git_pqueue list; + git_commit_list_node *cmit, *gave_up_on = NULL; + git_vector all_matches = GIT_VECTOR_INIT; + unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; + unsigned long seen_commits = 0; /* TODO: Check long */ + unsigned int unannotated_cnt = 0; + int error; + + if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0) + return -1; + + if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0) + goto cleanup; + + all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; + tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS; + + n = find_commit_name(data->names, git_commit_id(commit)); + if (n && (tags || all || n->prio == 2)) { + /* + * Exact match to an existing ref. + */ + if ((error = display_name(&buf, data->repo, n)) < 0) + goto cleanup; + + if (data->opts->always_use_long_format) { + if ((error = show_suffix(&buf, 0, + n->tag ? git_tag_target_id(n->tag) : git_commit_id(commit), + data->opts->abbreviated_size)) < 0) + goto cleanup; + } + + if (dirty_suffix) + git_buf_printf(&buf, "%s", dirty_suffix); + + if (git_buf_oom(&buf)) + return -1; + + goto found; + } + + if (!data->opts->max_candidates_tags) { + error = describe_not_found( + git_commit_id(commit), + "Cannot describe - no tag exactly matches '%s'"); + + goto cleanup; + } + + if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0) + goto cleanup; + + if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL) + goto cleanup; + + if ((error = git_commit_list_parse(walk, cmit)) < 0) + goto cleanup; + + cmit->flags = SEEN; + + if ((error = git_pqueue_insert(&list, cmit)) < 0) + goto cleanup; + + while (git_pqueue_size(&list) > 0) + { + int i; + + git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list); + seen_commits++; + + n = find_commit_name(data->names, &c->oid); + + if (n) { + if (!tags && !all && n->prio < 2) { + unannotated_cnt++; + } else if (match_cnt < data->opts->max_candidates_tags) { + struct possible_tag *t = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(t); + if ((error = git_vector_insert(&all_matches, t)) < 0) + goto cleanup; + + match_cnt++; + + t->name = n; + t->depth = seen_commits - 1; + t->flag_within = 1u << match_cnt; + t->found_order = match_cnt; + c->flags |= t->flag_within; + if (n->prio == 2) + annotated_cnt++; + } + else { + gave_up_on = c; + break; + } + } + + for (cur_match = 0; cur_match < match_cnt; cur_match++) { + struct possible_tag *t = git_vector_get(&all_matches, cur_match); + if (!(c->flags & t->flag_within)) + t->depth++; + } + + if (annotated_cnt && (git_pqueue_size(&list) == 0)) { + /* + if (debug) { + char oid_str[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oid_str, sizeof(oid_str), &c->oid); + + fprintf(stderr, "finished search at %s\n", oid_str); + } + */ + break; + } + for (i = 0; i < c->out_degree; i++) { + git_commit_list_node *p = c->parents[i]; + if ((error = git_commit_list_parse(walk, p)) < 0) + goto cleanup; + if (!(p->flags & SEEN)) + if ((error = git_pqueue_insert(&list, p)) < 0) + goto cleanup; + p->flags |= c->flags; + + if (data->opts->only_follow_first_parent) + break; + } + } + + if (!match_cnt) { + if (data->opts->show_commit_oid_as_fallback) { + char hex_oid[GIT_OID_HEXSZ]; + int size; + + if ((error = find_unique_abbrev_size( + &size, &cmit->oid, data->opts->abbreviated_size)) < 0) + goto cleanup; + + git_oid_fmt(hex_oid, &cmit->oid); + git_buf_put(&buf, hex_oid, size); + + if (dirty_suffix) + git_buf_printf(&buf, "%s", dirty_suffix); + + if (git_buf_oom(&buf)) + return -1; + + goto found; + } + if (unannotated_cnt) { + error = describe_not_found(git_commit_id(commit), + "Cannot describe - " + "No annotated tags can describe '%s'." + "However, there were unannotated tags."); + goto cleanup; + } + else { + error = describe_not_found(git_commit_id(commit), + "Cannot describe - " + "No tags can describe '%s'."); + goto cleanup; + } + } + + best = (struct possible_tag *)git_vector_get(&all_matches, 0); + + git_vector_sort(&all_matches); + + best = (struct possible_tag *)git_vector_get(&all_matches, 0); + + if (gave_up_on) { + git_pqueue_insert(&list, gave_up_on); + seen_commits--; + } + if ((error = finish_depth_computation( + &list, walk, best)) < 0) + goto cleanup; + seen_commits += error; + + /* + { + static const char *prio_names[] = { + "head", "lightweight", "annotated", + }; + + char oid_str[GIT_OID_HEXSZ + 1]; + + if (debug) { + for (cur_match = 0; cur_match < match_cnt; cur_match++) { + struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match); + fprintf(stderr, " %-11s %8d %s\n", + prio_names[t->name->prio], + t->depth, t->name->path); + } + fprintf(stderr, "traversed %lu commits\n", seen_commits); + if (gave_up_on) { + git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid); + fprintf(stderr, + "more than %i tags found; listed %i most recent\n" + "gave up search at %s\n", + data->opts->max_candidates_tags, data->opts->max_candidates_tags, + oid_str); + } + } + } + */ + + if ((error = display_name(&buf, data->repo, best->name)) < 0) + goto cleanup; + + if (data->opts->abbreviated_size) { + if ((error = show_suffix(&buf, best->depth, + &cmit->oid, data->opts->abbreviated_size)) < 0) + goto cleanup; + } + + if (dirty_suffix) + git_buf_printf(&buf, "%s", dirty_suffix); + + if (git_buf_oom(&buf)) + return -1; + +found: + out->ptr = buf.ptr; + out->asize = buf.asize; + out->size = buf.size; + +cleanup: + { + size_t i; + struct possible_tag *match; + git_vector_foreach(&all_matches, i, match) { + git__free(match); + } + } + git_vector_free(&all_matches); + git_pqueue_free(&list); + git_revwalk_free(walk); + return error; +} + +static int normalize_options( + git_describe_opts *dst, + const git_describe_opts *src) +{ + git_describe_opts default_options = GIT_DESCRIBE_OPTIONS_INIT; + if (!src) src = &default_options; + + *dst = *src; + + if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS) + dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS; + + if (dst->always_use_long_format && dst->abbreviated_size == 0) { + giterr_set(GITERR_DESCRIBE, "Cannot describe - " + "'always_use_long_format' is incompatible with a zero" + "'abbreviated_size'"); + return -1; + } + + return 0; +} + +/** TODO: Add git_object_describe_workdir(git_buf *, const char *dirty_suffix, git_describe_opts *); */ + +int git_describe_object( + git_buf *out, + git_object *committish, + git_describe_opts *opts) +{ + struct get_name_data data; + struct commit_name *name; + git_commit *commit; + int error = -1; + const char *dirty_suffix = NULL; + git_describe_opts normOptions; + + assert(out && committish); + + data.opts = opts; + data.repo = git_object_owner(committish); + + if ((error = normalize_options(&normOptions, opts)) < 0) + return error; + + GITERR_CHECK_VERSION( + &normOptions, + GIT_DESCRIBE_OPTIONS_VERSION, + "git_describe_opts"); + + data.names = git_oidmap_alloc(); + GITERR_CHECK_ALLOC(data.names); + + /** TODO: contains to be implemented */ + + /** TODO: deal with max_abbrev_size (either document or fix) */ + + if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0) + goto cleanup; + + if (git_reference_foreach_name( + git_object_owner(committish), + get_name, &data) < 0) + goto cleanup; + + if (git_oidmap_size(data.names) == 0) { + giterr_set(GITERR_DESCRIBE, "Cannot describe - " + "No reference found, cannot describe anything."); + error = -1; + goto cleanup; + } + + if ((error = describe(out, &data, commit, dirty_suffix)) < 0) + goto cleanup; + +cleanup: + git_oidmap_foreach_value(data.names, name, { + git_tag_free(name->tag); + git__free(name->path); + git__free(name); + }); + + git_oidmap_free(data.names); + git_commit_free(commit); + + return error; +} diff --git a/src/oidmap.h b/src/oidmap.h index 50da54b1c..b871a7926 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -45,4 +45,7 @@ GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) } } while (0) #define git_oidmap_foreach_value kh_foreach_value + +#define git_oidmap_size(h) kh_size(h) + #endif diff --git a/tests/describe/describe.c b/tests/describe/describe.c new file mode 100644 index 000000000..fcd4486bc --- /dev/null +++ b/tests/describe/describe.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "describe_helpers.h" + +void test_describe_describe__can_describe_against_a_bare_repo(void) +{ + git_repository *repo; + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + assert_describe("hard_tag", "HEAD", repo, &opts, false); + + opts.show_commit_oid_as_fallback = 1; + + assert_describe("be3563a", "HEAD^", repo, &opts, true); + + git_repository_free(repo); +} + +static int delete_cb(git_reference *ref, void *payload) +{ + GIT_UNUSED(payload); + + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + return 0; +} + +void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void) +{ + git_repository *repo; + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + git_object *object; + + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_revparse_single(&object, repo, "HEAD")); + + cl_git_pass(git_reference_foreach(repo, delete_cb, NULL)); + + cl_git_fail(git_describe_object(&buf, object, &opts)); + + git_object_free(object); + git_buf_free(&buf); + cl_git_sandbox_cleanup(); +} diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c new file mode 100644 index 000000000..aa2a54f9e --- /dev/null +++ b/tests/describe/describe_helpers.c @@ -0,0 +1,24 @@ +#include "describe_helpers.h" + +void assert_describe( + const char *expected_output, + const char *revparse_spec, + git_repository *repo, + git_describe_opts *opts, + bool is_prefix_match) +{ + git_object *object; + git_buf label; + + cl_git_pass(git_revparse_single(&object, repo, revparse_spec)); + + cl_git_pass(git_describe_object(&label, object, opts)); + + if (is_prefix_match) + cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); + else + cl_assert_equal_s(expected_output, label); + + git_object_free(object); + git_buf_free(&label); +} diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h new file mode 100644 index 000000000..0f107f5a7 --- /dev/null +++ b/tests/describe/describe_helpers.h @@ -0,0 +1,9 @@ +#include "clar_libgit2.h" +#include "buffer.h" + +extern void assert_describe( + const char *expected_output, + const char *revparse_spec, + git_repository *repo, + git_describe_opts *opts, + bool is_prefix_match); diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c new file mode 100644 index 000000000..d589e82a6 --- /dev/null +++ b/tests/describe/t6120.c @@ -0,0 +1,135 @@ +#include "clar_libgit2.h" +#include "describe_helpers.h" +#include "repository.h" + +// Ported from https://github.com/git/git/blob/adfc1857bdb090786fd9d22c1acec39371c76048/t/t6120-describe.sh + +static git_repository *repo; + +void test_describe_t6120__initialize(void) +{ + repo = cl_git_sandbox_init("describe"); +} + +void test_describe_t6120__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_describe_t6120__default(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + + assert_describe("A-", "HEAD", repo, &opts, true); + assert_describe("A-", "HEAD^", repo, &opts, true); + assert_describe("R-", "HEAD^^", repo, &opts, true); + assert_describe("A-", "HEAD^^2", repo, &opts, true); + assert_describe("B", "HEAD^^2^", repo, &opts, false); + assert_describe("R-", "HEAD^^^", repo, &opts, true); +} + +void test_describe_t6120__tags(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_TAGS; + + assert_describe("c-", "HEAD", repo, &opts, true); + assert_describe("c-", "HEAD^", repo, &opts, true); + assert_describe("e-", "HEAD^^", repo, &opts, true); + assert_describe("c-", "HEAD^^2", repo, &opts, true); + assert_describe("B", "HEAD^^2^", repo, &opts, false); + assert_describe("e", "HEAD^^^", repo, &opts, false); +} + +void test_describe_t6120__all(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_ALL; + + assert_describe("heads/master", "HEAD", repo, &opts, false); + assert_describe("tags/c-", "HEAD^", repo, &opts, true); + assert_describe("tags/e", "HEAD^^^", repo, &opts, false); +} + +void test_describe_t6120__longformat(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + opts.always_use_long_format = 1; + + assert_describe("B-0-", "HEAD^^2^", repo, &opts, true); + assert_describe("A-3-", "HEAD^^2", repo, &opts, true); +} + +void test_describe_t6120__firstparent(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_TAGS; + + assert_describe("c-7-", "HEAD", repo, &opts, true); + + opts.only_follow_first_parent = 1; + assert_describe("e-3-", "HEAD", repo, &opts, true); +} + +static void commit_and_tag( + git_time_t *time, + const char *commit_msg, + const char *tag_name) +{ + git_index *index; + git_oid commit_id; + git_reference *ref; + + cl_git_pass(git_repository_index__weakptr(&index, repo)); + + cl_git_append2file("describe/file", "\n"); + + git_index_add_bypath(index, "describe/file"); + git_index_write(index); + + *time += 10; + cl_repo_commit_from_index(&commit_id, repo, NULL, *time, commit_msg); + + if (tag_name == NULL) + return; + + cl_git_pass(git_reference_create(&ref, repo, tag_name, &commit_id, 0, NULL, NULL)); + git_reference_free(ref); +} + +void test_describe_t6120__pattern(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_oid tag_id; + git_object *head; + git_signature *tagger; + git_time_t time; + + /* set-up matching pattern tests */ + cl_git_pass(git_revparse_single(&head, repo, "HEAD")); + + time = 1380553019; + cl_git_pass(git_signature_new(&tagger, "tagger", "tagger@libgit2.org", time, 0)); + cl_git_pass(git_tag_create(&tag_id, repo, "test-annotated", head, tagger, "test-annotated", 0)); + git_signature_free(tagger); + git_object_free(head); + + commit_and_tag(&time, "one more", "refs/tags/test1-lightweight"); + commit_and_tag(&time, "yet another", "refs/tags/test2-lightweight"); + commit_and_tag(&time, "even more", NULL); + + + /* Exercize */ + opts.pattern = "test-*"; + assert_describe("test-annotated-", "HEAD", repo, &opts, true); + + opts.describe_strategy = GIT_DESCRIBE_TAGS; + opts.pattern = "test1-*"; + assert_describe("test1-lightweight-", "HEAD", repo, &opts, true); + + opts.pattern = "test2-*"; + assert_describe("test2-lightweight-", "HEAD", repo, &opts, true); + + opts.always_use_long_format = 1; + assert_describe("test2-lightweight-", "HEAD^", repo, &opts, true); +} From 1f501a086b4a9103f5ef5540c82c57d8559f0aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 04:58:02 +0200 Subject: [PATCH 04/11] describe: rename _object() to _commit() We don't describe arbitrary object, so let's give it the name of the one object type we accept. --- include/git2/describe.h | 2 +- src/describe.c | 2 +- tests/describe/describe.c | 2 +- tests/describe/describe_helpers.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/git2/describe.h b/include/git2/describe.h index 8a00d258a..0a845f6be 100644 --- a/include/git2/describe.h +++ b/include/git2/describe.h @@ -55,7 +55,7 @@ typedef struct git_describe_opts { GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE} -GIT_EXTERN(int) git_describe_object( +GIT_EXTERN(int) git_describe_commit( git_buf *out, git_object *committish, git_describe_opts *opts); diff --git a/src/describe.c b/src/describe.c index b503db761..c7b45d46f 100644 --- a/src/describe.c +++ b/src/describe.c @@ -626,7 +626,7 @@ static int normalize_options( /** TODO: Add git_object_describe_workdir(git_buf *, const char *dirty_suffix, git_describe_opts *); */ -int git_describe_object( +int git_describe_commit( git_buf *out, git_object *committish, git_describe_opts *opts) diff --git a/tests/describe/describe.c b/tests/describe/describe.c index fcd4486bc..a7f3c84df 100644 --- a/tests/describe/describe.c +++ b/tests/describe/describe.c @@ -39,7 +39,7 @@ void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void) cl_git_pass(git_reference_foreach(repo, delete_cb, NULL)); - cl_git_fail(git_describe_object(&buf, object, &opts)); + cl_git_fail(git_describe_commit(&buf, object, &opts)); git_object_free(object); git_buf_free(&buf); diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c index aa2a54f9e..7235d320f 100644 --- a/tests/describe/describe_helpers.c +++ b/tests/describe/describe_helpers.c @@ -12,7 +12,7 @@ void assert_describe( cl_git_pass(git_revparse_single(&object, repo, revparse_spec)); - cl_git_pass(git_describe_object(&label, object, opts)); + cl_git_pass(git_describe_commit(&label, object, opts)); if (is_prefix_match) cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); From 3b6534b80768e8751b398cd3aeffb888e374c8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 07:19:14 +0200 Subject: [PATCH 05/11] describe: split into gather and format steps Instead of printing out to the buffer inside the information-gathering phase, write the data to a intermediate result structure. This allows us to split the options into gathering options and formatting options, simplifying the gathering code. --- include/git2/describe.h | 27 +++- src/describe.c | 239 +++++++++++++++++++++--------- tests/describe/describe.c | 9 +- tests/describe/describe_helpers.c | 8 +- tests/describe/describe_helpers.h | 1 + tests/describe/t6120.c | 57 +++---- 6 files changed, 241 insertions(+), 100 deletions(-) diff --git a/include/git2/describe.h b/include/git2/describe.h index 0a845f6be..6007b004e 100644 --- a/include/git2/describe.h +++ b/include/git2/describe.h @@ -38,10 +38,8 @@ typedef struct git_describe_opts { unsigned int version; unsigned int max_candidates_tags; /** default: 10 */ - unsigned int abbreviated_size; unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */ const char *pattern; - int always_use_long_format; int only_follow_first_parent; int show_commit_oid_as_fallback; } git_describe_opts; @@ -53,13 +51,34 @@ typedef struct git_describe_opts { #define GIT_DESCRIBE_OPTIONS_INIT { \ GIT_DESCRIBE_OPTIONS_VERSION, \ GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ - GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE} +} + +typedef struct { + unsigned int version; + + unsigned int abbreviated_size; + + int always_use_long_format; + char *dirty_suffix; +} git_describe_format_options; + +#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1 +#define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \ + GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \ + GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \ + } + +typedef struct git_describe_result git_describe_result; GIT_EXTERN(int) git_describe_commit( - git_buf *out, + git_describe_result **result, git_object *committish, git_describe_opts *opts); +GIT_EXTERN(int) git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); + +GIT_EXTERN(void) git_describe_result_free(git_describe_result *result); + /** @} */ GIT_END_DECL diff --git a/src/describe.c b/src/describe.c index c7b45d46f..0c77f1537 100644 --- a/src/describe.c +++ b/src/describe.c @@ -162,13 +162,45 @@ cleanup: return error; } +typedef struct git_describe_result { + int dirty; + int exact_match; + int fallback_to_id; + git_oid commit_id; + git_repository *repo; + struct commit_name *name; + struct possible_tag *tag; +} git_describe_result; + struct get_name_data { git_describe_opts *opts; git_repository *repo; git_oidmap *names; + git_describe_result *result; }; +static int commit_name_dup(struct commit_name **out, struct commit_name *in) +{ + struct commit_name *name; + + name = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(name); + + memcpy(name, in, sizeof(struct commit_name)); + name->tag = NULL; + name->path = NULL; + + if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0) + return -1; + + name->path = git__strdup(in->path); + GITERR_CHECK_ALLOC(name->path); + + *out = name; + return 0; +} + static int get_name(const char *refname, void *payload) { struct get_name_data *data; @@ -223,6 +255,23 @@ struct possible_tag { unsigned flag_within; }; +static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in) +{ + struct possible_tag *tag; + + tag = git__malloc(sizeof(struct possible_tag)); + GITERR_CHECK_ALLOC(tag); + + memcpy(tag, in, sizeof(struct possible_tag)); + tag->name = NULL; + + if (commit_name_dup(&tag->name, in->name) < 0) + return -1; + + *out = tag; + return 0; +} + static int compare_pt(const void *a_, const void *b_) { struct possible_tag *a = (struct possible_tag *)a_; @@ -352,15 +401,12 @@ static int describe_not_found(const git_oid *oid, const char *message_format) { } static int describe( - git_buf *out, struct get_name_data *data, - git_commit *commit, - const char *dirty_suffix) + git_commit *commit) { struct commit_name *n; struct possible_tag *best; bool all, tags; - git_buf buf = GIT_BUF_INIT; git_revwalk *walk = NULL; git_pqueue list; git_commit_list_node *cmit, *gave_up_on = NULL; @@ -379,28 +425,18 @@ static int describe( all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS; + git_oid_cpy(&data->result->commit_id, git_commit_id(commit)); + n = find_commit_name(data->names, git_commit_id(commit)); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ - if ((error = display_name(&buf, data->repo, n)) < 0) + data->result->exact_match = 1; + if ((error = commit_name_dup(&data->result->name, n)) < 0) goto cleanup; - if (data->opts->always_use_long_format) { - if ((error = show_suffix(&buf, 0, - n->tag ? git_tag_target_id(n->tag) : git_commit_id(commit), - data->opts->abbreviated_size)) < 0) - goto cleanup; - } - - if (dirty_suffix) - git_buf_printf(&buf, "%s", dirty_suffix); - - if (git_buf_oom(&buf)) - return -1; - - goto found; + goto cleanup; } if (!data->opts->max_candidates_tags) { @@ -492,23 +528,10 @@ static int describe( if (!match_cnt) { if (data->opts->show_commit_oid_as_fallback) { - char hex_oid[GIT_OID_HEXSZ]; - int size; + data->result->fallback_to_id = 1; + git_oid_cpy(&data->result->commit_id, &cmit->oid); - if ((error = find_unique_abbrev_size( - &size, &cmit->oid, data->opts->abbreviated_size)) < 0) - goto cleanup; - - git_oid_fmt(hex_oid, &cmit->oid); - git_buf_put(&buf, hex_oid, size); - - if (dirty_suffix) - git_buf_printf(&buf, "%s", dirty_suffix); - - if (git_buf_oom(&buf)) - return -1; - - goto found; + goto cleanup; } if (unannotated_cnt) { error = describe_not_found(git_commit_id(commit), @@ -538,7 +561,10 @@ static int describe( if ((error = finish_depth_computation( &list, walk, best)) < 0) goto cleanup; + seen_commits += error; + if ((error = possible_tag_dup(&data->result->tag, best)) < 0) + goto cleanup; /* { @@ -568,25 +594,7 @@ static int describe( } */ - if ((error = display_name(&buf, data->repo, best->name)) < 0) - goto cleanup; - - if (data->opts->abbreviated_size) { - if ((error = show_suffix(&buf, best->depth, - &cmit->oid, data->opts->abbreviated_size)) < 0) - goto cleanup; - } - - if (dirty_suffix) - git_buf_printf(&buf, "%s", dirty_suffix); - - if (git_buf_oom(&buf)) - return -1; - -found: - out->ptr = buf.ptr; - out->asize = buf.asize; - out->size = buf.size; + git_oid_cpy(&data->result->commit_id, &cmit->oid); cleanup: { @@ -614,20 +622,13 @@ static int normalize_options( if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS) dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS; - if (dst->always_use_long_format && dst->abbreviated_size == 0) { - giterr_set(GITERR_DESCRIBE, "Cannot describe - " - "'always_use_long_format' is incompatible with a zero" - "'abbreviated_size'"); - return -1; - } - return 0; } /** TODO: Add git_object_describe_workdir(git_buf *, const char *dirty_suffix, git_describe_opts *); */ int git_describe_commit( - git_buf *out, + git_describe_result **result, git_object *committish, git_describe_opts *opts) { @@ -635,10 +636,13 @@ int git_describe_commit( struct commit_name *name; git_commit *commit; int error = -1; - const char *dirty_suffix = NULL; git_describe_opts normOptions; - assert(out && committish); + assert(committish); + + data.result = git__calloc(1, sizeof(git_describe_result)); + GITERR_CHECK_ALLOC(data.result); + data.result->repo = git_object_owner(committish); data.opts = opts; data.repo = git_object_owner(committish); @@ -673,10 +677,12 @@ int git_describe_commit( goto cleanup; } - if ((error = describe(out, &data, commit, dirty_suffix)) < 0) + if ((error = describe(&data, commit)) < 0) goto cleanup; cleanup: + git_commit_free(commit); + git_oidmap_foreach_value(data.names, name, { git_tag_free(name->tag); git__free(name->path); @@ -684,7 +690,108 @@ cleanup: }); git_oidmap_free(data.names); - git_commit_free(commit); + + if (error < 0) + git_describe_result_free(data.result); + else + *result = data.result; return error; } + +int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts) +{ + int error; + git_repository *repo; + struct commit_name *name; + + assert(out && result); + + GITERR_CHECK_VERSION(opts, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options"); + git_buf_sanitize(out); + + + if (opts->always_use_long_format && opts->abbreviated_size == 0) { + giterr_set(GITERR_DESCRIBE, "Cannot describe - " + "'always_use_long_format' is incompatible with a zero" + "'abbreviated_size'"); + return -1; + } + + + repo = result->repo; + + /* If we did find an exact match, then it's the easier method */ + if (result->exact_match) { + name = result->name; + if ((error = display_name(out, repo, name)) < 0) + return error; + + if (opts->always_use_long_format) { + const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id; + if ((error = show_suffix(out, 0, id, opts->abbreviated_size)) < 0) + return error; + } + + if (result->dirty && opts->dirty_suffix) + git_buf_puts(out, opts->dirty_suffix); + + return git_buf_oom(out) ? -1 : 0; + } + + /* If we didn't find *any* tags, we fall back to the commit's id */ + if (result->fallback_to_id) { + char hex_oid[GIT_OID_HEXSZ + 1] = {0}; + int size; + if ((error = find_unique_abbrev_size( + &size, &result->commit_id, opts->abbreviated_size)) < 0) + return -1; + + git_oid_fmt(hex_oid, &result->commit_id); + git_buf_put(out, hex_oid, size); + + if (opts->dirty_suffix) + git_buf_puts(out, opts->dirty_suffix); + + return git_buf_oom(out) ? -1 : 0; + } + + /* Lastly, if we found a matching tag, we show that */ + name = result->tag->name; + + if ((error = display_name(out, repo, name)) < 0) + return error; + + if (opts->abbreviated_size) { + if ((error = show_suffix(out, result->tag->depth, + &result->commit_id, opts->abbreviated_size)) < 0) + return -1; + } + + if (opts->dirty_suffix) + git_buf_puts(out, opts->dirty_suffix); + + + return git_buf_oom(out) ? -1 : 0; +} + +void git_describe_result_free(git_describe_result *result) +{ + if (result == NULL) + return; + + if (result->name) { + git_tag_free(result->name->tag); + git__free(result->name->path); + git__free(result->name); + } + + if (result->tag) { + git_tag_free(result->tag->name->tag); + git__free(result->tag->name->path); + git__free(result->tag->name); + git__free(result->tag); + } + + git__free(result); +} diff --git a/tests/describe/describe.c b/tests/describe/describe.c index a7f3c84df..e2dda96cd 100644 --- a/tests/describe/describe.c +++ b/tests/describe/describe.c @@ -5,14 +5,15 @@ void test_describe_describe__can_describe_against_a_bare_repo(void) { git_repository *repo; git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - assert_describe("hard_tag", "HEAD", repo, &opts, false); + assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts, false); opts.show_commit_oid_as_fallback = 1; - assert_describe("be3563a", "HEAD^", repo, &opts, true); + assert_describe("be3563a", "HEAD^", repo, &opts, &fmt_opts, true); git_repository_free(repo); } @@ -33,14 +34,16 @@ void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void) git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; git_buf buf = GIT_BUF_INIT; git_object *object; + git_describe_result *result = NULL; repo = cl_git_sandbox_init("testrepo.git"); cl_git_pass(git_revparse_single(&object, repo, "HEAD")); cl_git_pass(git_reference_foreach(repo, delete_cb, NULL)); - cl_git_fail(git_describe_commit(&buf, object, &opts)); + cl_git_fail(git_describe_commit(&result, object, &opts)); + git_describe_result_free(result); git_object_free(object); git_buf_free(&buf); cl_git_sandbox_cleanup(); diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c index 7235d320f..d975ddf4b 100644 --- a/tests/describe/describe_helpers.c +++ b/tests/describe/describe_helpers.c @@ -5,20 +5,24 @@ void assert_describe( const char *revparse_spec, git_repository *repo, git_describe_opts *opts, + git_describe_format_options *fmt_opts, bool is_prefix_match) { git_object *object; - git_buf label; + git_buf label = GIT_BUF_INIT; + git_describe_result *result; cl_git_pass(git_revparse_single(&object, repo, revparse_spec)); - cl_git_pass(git_describe_commit(&label, object, opts)); + cl_git_pass(git_describe_commit(&result, object, opts)); + cl_git_pass(git_describe_format(&label, result, fmt_opts)); if (is_prefix_match) cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); else cl_assert_equal_s(expected_output, label); + git_describe_result_free(result); git_object_free(object); git_buf_free(&label); } diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h index 0f107f5a7..a666b46cf 100644 --- a/tests/describe/describe_helpers.h +++ b/tests/describe/describe_helpers.h @@ -6,4 +6,5 @@ extern void assert_describe( const char *revparse_spec, git_repository *repo, git_describe_opts *opts, + git_describe_format_options *fmt_opts, bool is_prefix_match); diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c index d589e82a6..39489f3c5 100644 --- a/tests/describe/t6120.c +++ b/tests/describe/t6120.c @@ -19,56 +19,62 @@ void test_describe_t6120__cleanup(void) void test_describe_t6120__default(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; - assert_describe("A-", "HEAD", repo, &opts, true); - assert_describe("A-", "HEAD^", repo, &opts, true); - assert_describe("R-", "HEAD^^", repo, &opts, true); - assert_describe("A-", "HEAD^^2", repo, &opts, true); - assert_describe("B", "HEAD^^2^", repo, &opts, false); - assert_describe("R-", "HEAD^^^", repo, &opts, true); + assert_describe("A-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("A-", "HEAD^", repo, &opts, &fmt_opts, true); + assert_describe("R-", "HEAD^^", repo, &opts, &fmt_opts, true); + assert_describe("A-", "HEAD^^2", repo, &opts, &fmt_opts, true); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts, false); + assert_describe("R-", "HEAD^^^", repo, &opts, &fmt_opts, true); } void test_describe_t6120__tags(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; - assert_describe("c-", "HEAD", repo, &opts, true); - assert_describe("c-", "HEAD^", repo, &opts, true); - assert_describe("e-", "HEAD^^", repo, &opts, true); - assert_describe("c-", "HEAD^^2", repo, &opts, true); - assert_describe("B", "HEAD^^2^", repo, &opts, false); - assert_describe("e", "HEAD^^^", repo, &opts, false); + assert_describe("c-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("c-", "HEAD^", repo, &opts, &fmt_opts, true); + assert_describe("e-", "HEAD^^", repo, &opts, &fmt_opts, true); + assert_describe("c-", "HEAD^^2", repo, &opts, &fmt_opts, true); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts, false); + assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts, false); } void test_describe_t6120__all(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_ALL; - assert_describe("heads/master", "HEAD", repo, &opts, false); - assert_describe("tags/c-", "HEAD^", repo, &opts, true); - assert_describe("tags/e", "HEAD^^^", repo, &opts, false); + assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts, false); + assert_describe("tags/c-", "HEAD^", repo, &opts, &fmt_opts, true); + assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts, false); } void test_describe_t6120__longformat(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; - opts.always_use_long_format = 1; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; - assert_describe("B-0-", "HEAD^^2^", repo, &opts, true); - assert_describe("A-3-", "HEAD^^2", repo, &opts, true); + fmt_opts.always_use_long_format = 1; + + assert_describe("B-0-", "HEAD^^2^", repo, &opts, &fmt_opts, true); + assert_describe("A-3-", "HEAD^^2", repo, &opts, &fmt_opts, true); } void test_describe_t6120__firstparent(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; - assert_describe("c-7-", "HEAD", repo, &opts, true); + assert_describe("c-7-", "HEAD", repo, &opts, &fmt_opts, true); opts.only_follow_first_parent = 1; - assert_describe("e-3-", "HEAD", repo, &opts, true); + assert_describe("e-3-", "HEAD", repo, &opts, &fmt_opts, true); } static void commit_and_tag( @@ -100,6 +106,7 @@ static void commit_and_tag( void test_describe_t6120__pattern(void) { git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; git_oid tag_id; git_object *head; git_signature *tagger; @@ -121,15 +128,15 @@ void test_describe_t6120__pattern(void) /* Exercize */ opts.pattern = "test-*"; - assert_describe("test-annotated-", "HEAD", repo, &opts, true); + assert_describe("test-annotated-", "HEAD", repo, &opts, &fmt_opts, true); opts.describe_strategy = GIT_DESCRIBE_TAGS; opts.pattern = "test1-*"; - assert_describe("test1-lightweight-", "HEAD", repo, &opts, true); + assert_describe("test1-lightweight-", "HEAD", repo, &opts, &fmt_opts, true); opts.pattern = "test2-*"; - assert_describe("test2-lightweight-", "HEAD", repo, &opts, true); + assert_describe("test2-lightweight-", "HEAD", repo, &opts, &fmt_opts, true); - opts.always_use_long_format = 1; - assert_describe("test2-lightweight-", "HEAD^", repo, &opts, true); + fmt_opts.always_use_long_format = 1; + assert_describe("test2-lightweight-", "HEAD^", repo, &opts, &fmt_opts, true); } From fd8126e4c605e749ed6ab1e31ac32366adc8cc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 08:54:52 +0200 Subject: [PATCH 06/11] describe: implement describing the workdir When we describe the workdir, we perform a describe on HEAD and then check to see if the worktree is dirty. If it is and we have a suffix string, we append that to the buffer. --- include/git2/describe.h | 5 +++ src/describe.c | 55 +++++++++++++++++++++++++++---- tests/describe/describe_helpers.c | 26 +++++++++++++++ tests/describe/describe_helpers.h | 8 +++++ tests/describe/t6120.c | 14 ++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/include/git2/describe.h b/include/git2/describe.h index 6007b004e..8b80e1806 100644 --- a/include/git2/describe.h +++ b/include/git2/describe.h @@ -75,6 +75,11 @@ GIT_EXTERN(int) git_describe_commit( git_object *committish, git_describe_opts *opts); +GIT_EXTERN(int) git_describe_workdir( + git_describe_result **out, + git_repository *repo, + git_describe_opts *opts); + GIT_EXTERN(int) git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); GIT_EXTERN(void) git_describe_result_free(git_describe_result *result); diff --git a/src/describe.c b/src/describe.c index 0c77f1537..40092d003 100644 --- a/src/describe.c +++ b/src/describe.c @@ -5,6 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ #include "git2/describe.h" +#include "git2/strarray.h" +#include "git2/diff.h" +#include "git2/status.h" #include "common.h" #include "commit.h" @@ -385,7 +388,6 @@ static int show_suffix( git_buf_printf(buf, "-%d-g", depth); git_buf_put(buf, hex_oid, size); - git_buf_putc(buf, '\0'); return git_buf_oom(buf) ? -1 : 0; } @@ -699,6 +701,47 @@ cleanup: return error; } +int git_describe_workdir( + git_describe_result **out, + git_repository *repo, + git_describe_opts *opts) +{ + int error; + git_oid current_id; + git_status_list *status = NULL; + git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; + git_describe_result *result; + git_object *commit; + + if ((error = git_reference_name_to_id(¤t_id, repo, GIT_HEAD_FILE)) < 0) + return error; + + if ((error = git_object_lookup(&commit, repo, ¤t_id, GIT_OBJ_COMMIT)) < 0) + return error; + + /* The first step is to perform a describe of HEAD, so we can leverage this */ + if ((error = git_describe_commit(&result, commit, opts)) < 0) + goto out; + + if ((error = git_status_list_new(&status, repo, &status_opts)) < 0) + goto out; + + + if (git_status_list_entrycount(status) > 0) + result->dirty = 1; + +out: + git_object_free(commit); + git_status_list_free(status); + + if (error < 0) + git_describe_result_free(result); + else + *out = result; + + return error; +} + int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts) { int error; @@ -744,13 +787,13 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g char hex_oid[GIT_OID_HEXSZ + 1] = {0}; int size; if ((error = find_unique_abbrev_size( - &size, &result->commit_id, opts->abbreviated_size)) < 0) + &size, repo, &result->commit_id, opts->abbreviated_size)) < 0) return -1; git_oid_fmt(hex_oid, &result->commit_id); git_buf_put(out, hex_oid, size); - if (opts->dirty_suffix) + if (result->dirty && opts->dirty_suffix) git_buf_puts(out, opts->dirty_suffix); return git_buf_oom(out) ? -1 : 0; @@ -765,12 +808,12 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g if (opts->abbreviated_size) { if ((error = show_suffix(out, result->tag->depth, &result->commit_id, opts->abbreviated_size)) < 0) - return -1; + return error; } - if (opts->dirty_suffix) + if (result->dirty && opts->dirty_suffix) { git_buf_puts(out, opts->dirty_suffix); - + } return git_buf_oom(out) ? -1 : 0; } diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c index d975ddf4b..230e1bd15 100644 --- a/tests/describe/describe_helpers.c +++ b/tests/describe/describe_helpers.c @@ -26,3 +26,29 @@ void assert_describe( git_object_free(object); git_buf_free(&label); } + +void assert_describe_workdir( + const char *expected_output, + const char *expected_suffix, + git_repository *repo, + git_describe_opts *opts, + git_describe_format_options *fmt_opts, + bool is_prefix_match) +{ + git_buf label = GIT_BUF_INIT; + git_describe_result *result; + + cl_git_pass(git_describe_workdir(&result, repo, opts)); + cl_git_pass(git_describe_format(&label, result, fmt_opts)); + + if (is_prefix_match) + cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); + else + cl_assert_equal_s(expected_output, label); + + if (expected_suffix) + cl_assert_equal_i(0, git__suffixcmp(git_buf_cstr(&label), expected_suffix)); + + git_describe_result_free(result); + git_buf_free(&label); +} diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h index a666b46cf..6f793582c 100644 --- a/tests/describe/describe_helpers.h +++ b/tests/describe/describe_helpers.h @@ -8,3 +8,11 @@ extern void assert_describe( git_describe_opts *opts, git_describe_format_options *fmt_opts, bool is_prefix_match); + +extern void assert_describe_workdir( + const char *expected_output, + const char *expected_suffix, + git_repository *repo, + git_describe_opts *opts, + git_describe_format_options *fmt_opts, + bool is_prefix_match); diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c index 39489f3c5..2ac59a6bc 100644 --- a/tests/describe/t6120.c +++ b/tests/describe/t6120.c @@ -77,6 +77,20 @@ void test_describe_t6120__firstparent(void) assert_describe("e-3-", "HEAD", repo, &opts, &fmt_opts, true); } +void test_describe_t6120__workdir(void) +{ + git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + + assert_describe_workdir("A-", NULL, repo, &opts, &fmt_opts, true); + cl_git_mkfile("describe/file", "something different"); + + fmt_opts.dirty_suffix = "-dirty"; + assert_describe_workdir("A-", "-dirty", repo, &opts, &fmt_opts, true); + fmt_opts.dirty_suffix = ".mod"; + assert_describe_workdir("A-", ".mod", repo, &opts, &fmt_opts, true); +} + static void commit_and_tag( git_time_t *time, const char *commit_msg, From 55f1b6b6414035c675b3102ad4f25f4d02cbe695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 08:56:27 +0200 Subject: [PATCH 07/11] describe: implement abbreviated ids --- src/describe.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/describe.c b/src/describe.c index 40092d003..b8bead95d 100644 --- a/src/describe.c +++ b/src/describe.c @@ -17,6 +17,7 @@ #include "revwalk.h" #include "tag.h" #include "vector.h" +#include "repository.h" /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ @@ -357,14 +358,33 @@ static int display_name(git_buf *buf, git_repository *repo, struct commit_name * static int find_unique_abbrev_size( int *out, + git_repository *repo, const git_oid *oid_in, int abbreviated_size) { - GIT_UNUSED(abbreviated_size); + size_t size = abbreviated_size; + git_odb *odb; + git_oid dummy; + int error; - /* TODO: Actually find the abbreviated oid size */ - GIT_UNUSED(oid_in); + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + while (size < GIT_OID_HEXSZ) { + if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) { + *out = (int) size; + return 0; + } + + /* If the error wasn't that it's not unique, then it's a proper error */ + if (error != GIT_EAMBIGUOUS) + return error; + + /* Try again with a larger size */ + size++; + } + + /* If we didn't find any shorter prefix, we have to do the whole thing */ *out = GIT_OID_HEXSZ; return 0; @@ -373,6 +393,7 @@ static int find_unique_abbrev_size( static int show_suffix( git_buf *buf, int depth, + git_repository *repo, const git_oid* id, size_t abbrev_size) { @@ -380,7 +401,7 @@ static int show_suffix( char hex_oid[GIT_OID_HEXSZ]; - if ((error = find_unique_abbrev_size(&size, id, abbrev_size)) < 0) + if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0) return error; git_oid_fmt(hex_oid, id); @@ -772,7 +793,7 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g if (opts->always_use_long_format) { const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id; - if ((error = show_suffix(out, 0, id, opts->abbreviated_size)) < 0) + if ((error = show_suffix(out, 0, repo, id, opts->abbreviated_size)) < 0) return error; } @@ -806,7 +827,7 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g return error; if (opts->abbreviated_size) { - if ((error = show_suffix(out, result->tag->depth, + if ((error = show_suffix(out, result->tag->depth, repo, &result->commit_id, opts->abbreviated_size)) < 0) return error; } From 5431c46a9604ac5c5d17390013d3741196b41051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 09:04:04 +0200 Subject: [PATCH 08/11] describe: use globs in the tests This makes us be closer to git's tests, and lets us better describe what we expect from the output. --- tests/describe/describe.c | 4 +-- tests/describe/describe_helpers.c | 20 +++--------- tests/describe/describe_helpers.h | 7 ++--- tests/describe/t6120.c | 52 +++++++++++++++---------------- 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/tests/describe/describe.c b/tests/describe/describe.c index e2dda96cd..d2df36c91 100644 --- a/tests/describe/describe.c +++ b/tests/describe/describe.c @@ -9,11 +9,11 @@ void test_describe_describe__can_describe_against_a_bare_repo(void) cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts, false); + assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts); opts.show_commit_oid_as_fallback = 1; - assert_describe("be3563a", "HEAD^", repo, &opts, &fmt_opts, true); + assert_describe("be3563a*", "HEAD^", repo, &opts, &fmt_opts); git_repository_free(repo); } diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c index 230e1bd15..e4dfcef3e 100644 --- a/tests/describe/describe_helpers.c +++ b/tests/describe/describe_helpers.c @@ -5,8 +5,7 @@ void assert_describe( const char *revparse_spec, git_repository *repo, git_describe_opts *opts, - git_describe_format_options *fmt_opts, - bool is_prefix_match) + git_describe_format_options *fmt_opts) { git_object *object; git_buf label = GIT_BUF_INIT; @@ -17,10 +16,7 @@ void assert_describe( cl_git_pass(git_describe_commit(&result, object, opts)); cl_git_pass(git_describe_format(&label, result, fmt_opts)); - if (is_prefix_match) - cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); - else - cl_assert_equal_s(expected_output, label); + cl_git_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0)); git_describe_result_free(result); git_object_free(object); @@ -29,11 +25,9 @@ void assert_describe( void assert_describe_workdir( const char *expected_output, - const char *expected_suffix, git_repository *repo, git_describe_opts *opts, - git_describe_format_options *fmt_opts, - bool is_prefix_match) + git_describe_format_options *fmt_opts) { git_buf label = GIT_BUF_INIT; git_describe_result *result; @@ -41,13 +35,7 @@ void assert_describe_workdir( cl_git_pass(git_describe_workdir(&result, repo, opts)); cl_git_pass(git_describe_format(&label, result, fmt_opts)); - if (is_prefix_match) - cl_assert_equal_i(0, git__prefixcmp(git_buf_cstr(&label), expected_output)); - else - cl_assert_equal_s(expected_output, label); - - if (expected_suffix) - cl_assert_equal_i(0, git__suffixcmp(git_buf_cstr(&label), expected_suffix)); + cl_git_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0)); git_describe_result_free(result); git_buf_free(&label); diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h index 6f793582c..5d6417261 100644 --- a/tests/describe/describe_helpers.h +++ b/tests/describe/describe_helpers.h @@ -6,13 +6,10 @@ extern void assert_describe( const char *revparse_spec, git_repository *repo, git_describe_opts *opts, - git_describe_format_options *fmt_opts, - bool is_prefix_match); + git_describe_format_options *fmt_opts); extern void assert_describe_workdir( const char *expected_output, - const char *expected_suffix, git_repository *repo, git_describe_opts *opts, - git_describe_format_options *fmt_opts, - bool is_prefix_match); + git_describe_format_options *fmt_opts); diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c index 2ac59a6bc..b470938d0 100644 --- a/tests/describe/t6120.c +++ b/tests/describe/t6120.c @@ -21,12 +21,12 @@ void test_describe_t6120__default(void) git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; - assert_describe("A-", "HEAD", repo, &opts, &fmt_opts, true); - assert_describe("A-", "HEAD^", repo, &opts, &fmt_opts, true); - assert_describe("R-", "HEAD^^", repo, &opts, &fmt_opts, true); - assert_describe("A-", "HEAD^^2", repo, &opts, &fmt_opts, true); - assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts, false); - assert_describe("R-", "HEAD^^^", repo, &opts, &fmt_opts, true); + assert_describe("A-*", "HEAD", repo, &opts, &fmt_opts); + assert_describe("A-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("R-*", "HEAD^^", repo, &opts, &fmt_opts); + assert_describe("A-*", "HEAD^^2", repo, &opts, &fmt_opts); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("R-*", "HEAD^^^", repo, &opts, &fmt_opts); } void test_describe_t6120__tags(void) @@ -35,12 +35,12 @@ void test_describe_t6120__tags(void) git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; - assert_describe("c-", "HEAD", repo, &opts, &fmt_opts, true); - assert_describe("c-", "HEAD^", repo, &opts, &fmt_opts, true); - assert_describe("e-", "HEAD^^", repo, &opts, &fmt_opts, true); - assert_describe("c-", "HEAD^^2", repo, &opts, &fmt_opts, true); - assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts, false); - assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts, false); + assert_describe("c-*", "HEAD", repo, &opts, &fmt_opts); + assert_describe("c-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("e-*", "HEAD^^", repo, &opts, &fmt_opts); + assert_describe("c-*", "HEAD^^2", repo, &opts, &fmt_opts); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts); } void test_describe_t6120__all(void) @@ -49,9 +49,9 @@ void test_describe_t6120__all(void) git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_ALL; - assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts, false); - assert_describe("tags/c-", "HEAD^", repo, &opts, &fmt_opts, true); - assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts, false); + assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts); + assert_describe("tags/c-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts); } void test_describe_t6120__longformat(void) @@ -61,8 +61,8 @@ void test_describe_t6120__longformat(void) fmt_opts.always_use_long_format = 1; - assert_describe("B-0-", "HEAD^^2^", repo, &opts, &fmt_opts, true); - assert_describe("A-3-", "HEAD^^2", repo, &opts, &fmt_opts, true); + assert_describe("B-0-*", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("A-3-*", "HEAD^^2", repo, &opts, &fmt_opts); } void test_describe_t6120__firstparent(void) @@ -71,10 +71,10 @@ void test_describe_t6120__firstparent(void) git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; - assert_describe("c-7-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("c-7-*", "HEAD", repo, &opts, &fmt_opts); opts.only_follow_first_parent = 1; - assert_describe("e-3-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("e-3-*", "HEAD", repo, &opts, &fmt_opts); } void test_describe_t6120__workdir(void) @@ -82,13 +82,13 @@ void test_describe_t6120__workdir(void) git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; - assert_describe_workdir("A-", NULL, repo, &opts, &fmt_opts, true); + assert_describe_workdir("A-*[0-9a-f]", repo, &opts, &fmt_opts); cl_git_mkfile("describe/file", "something different"); fmt_opts.dirty_suffix = "-dirty"; - assert_describe_workdir("A-", "-dirty", repo, &opts, &fmt_opts, true); + assert_describe_workdir("A-*[0-9a-f]-dirty", repo, &opts, &fmt_opts); fmt_opts.dirty_suffix = ".mod"; - assert_describe_workdir("A-", ".mod", repo, &opts, &fmt_opts, true); + assert_describe_workdir("A-*[0-9a-f].mod", repo, &opts, &fmt_opts); } static void commit_and_tag( @@ -142,15 +142,15 @@ void test_describe_t6120__pattern(void) /* Exercize */ opts.pattern = "test-*"; - assert_describe("test-annotated-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("test-annotated-*", "HEAD", repo, &opts, &fmt_opts); opts.describe_strategy = GIT_DESCRIBE_TAGS; opts.pattern = "test1-*"; - assert_describe("test1-lightweight-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("test1-lightweight-*", "HEAD", repo, &opts, &fmt_opts); opts.pattern = "test2-*"; - assert_describe("test2-lightweight-", "HEAD", repo, &opts, &fmt_opts, true); + assert_describe("test2-lightweight-*", "HEAD", repo, &opts, &fmt_opts); fmt_opts.always_use_long_format = 1; - assert_describe("test2-lightweight-", "HEAD^", repo, &opts, &fmt_opts, true); + assert_describe("test2-lightweight-*", "HEAD^", repo, &opts, &fmt_opts); } From 25345c0cbe1493d63bbc9d309d7fcf0f84df741b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 09:18:22 +0200 Subject: [PATCH 09/11] describe: rename git_describe_opts to git_describe_options And implement the option init functions for this and the format options. --- include/git2/describe.h | 14 ++++++++---- src/describe.c | 38 +++++++++++++++++++------------ tests/describe/describe.c | 4 ++-- tests/describe/describe_helpers.c | 4 ++-- tests/describe/describe_helpers.h | 4 ++-- tests/describe/t6120.c | 14 ++++++------ 6 files changed, 46 insertions(+), 32 deletions(-) diff --git a/include/git2/describe.h b/include/git2/describe.h index 8b80e1806..bdbdfcfff 100644 --- a/include/git2/describe.h +++ b/include/git2/describe.h @@ -32,9 +32,9 @@ typedef enum { * Zero out for defaults. Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to * correctly set the `version` field. E.g. * - * git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + * git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; */ -typedef struct git_describe_opts { +typedef struct git_describe_options { unsigned int version; unsigned int max_candidates_tags; /** default: 10 */ @@ -42,7 +42,7 @@ typedef struct git_describe_opts { const char *pattern; int only_follow_first_parent; int show_commit_oid_as_fallback; -} git_describe_opts; +} git_describe_options; #define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10 #define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7 @@ -53,6 +53,8 @@ typedef struct git_describe_opts { GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ } +GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version); + typedef struct { unsigned int version; @@ -68,17 +70,19 @@ typedef struct { GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \ } +GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version); + typedef struct git_describe_result git_describe_result; GIT_EXTERN(int) git_describe_commit( git_describe_result **result, git_object *committish, - git_describe_opts *opts); + git_describe_options *opts); GIT_EXTERN(int) git_describe_workdir( git_describe_result **out, git_repository *repo, - git_describe_opts *opts); + git_describe_options *opts); GIT_EXTERN(int) git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); diff --git a/src/describe.c b/src/describe.c index b8bead95d..afb2e2186 100644 --- a/src/describe.c +++ b/src/describe.c @@ -178,7 +178,7 @@ typedef struct git_describe_result { struct get_name_data { - git_describe_opts *opts; + git_describe_options *opts; git_repository *repo; git_oidmap *names; git_describe_result *result; @@ -634,10 +634,10 @@ cleanup: } static int normalize_options( - git_describe_opts *dst, - const git_describe_opts *src) + git_describe_options *dst, + const git_describe_options *src) { - git_describe_opts default_options = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT; if (!src) src = &default_options; *dst = *src; @@ -648,18 +648,16 @@ static int normalize_options( return 0; } -/** TODO: Add git_object_describe_workdir(git_buf *, const char *dirty_suffix, git_describe_opts *); */ - int git_describe_commit( git_describe_result **result, git_object *committish, - git_describe_opts *opts) + git_describe_options *opts) { struct get_name_data data; struct commit_name *name; git_commit *commit; int error = -1; - git_describe_opts normOptions; + git_describe_options normalized; assert(committish); @@ -670,21 +668,19 @@ int git_describe_commit( data.opts = opts; data.repo = git_object_owner(committish); - if ((error = normalize_options(&normOptions, opts)) < 0) + if ((error = normalize_options(&normalized, opts)) < 0) return error; GITERR_CHECK_VERSION( - &normOptions, + &normalized, GIT_DESCRIBE_OPTIONS_VERSION, - "git_describe_opts"); + "git_describe_options"); data.names = git_oidmap_alloc(); GITERR_CHECK_ALLOC(data.names); /** TODO: contains to be implemented */ - /** TODO: deal with max_abbrev_size (either document or fix) */ - if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0) goto cleanup; @@ -725,7 +721,7 @@ cleanup: int git_describe_workdir( git_describe_result **out, git_repository *repo, - git_describe_opts *opts) + git_describe_options *opts) { int error; git_oid current_id; @@ -859,3 +855,17 @@ void git_describe_result_free(git_describe_result *result) git__free(result); } + +int git_describe_init_options(git_describe_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT); + return 0; +} + +int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT); + return 0; +} diff --git a/tests/describe/describe.c b/tests/describe/describe.c index d2df36c91..9a523a169 100644 --- a/tests/describe/describe.c +++ b/tests/describe/describe.c @@ -4,7 +4,7 @@ void test_describe_describe__can_describe_against_a_bare_repo(void) { git_repository *repo; - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); @@ -31,7 +31,7 @@ static int delete_cb(git_reference *ref, void *payload) void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void) { git_repository *repo; - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_buf buf = GIT_BUF_INIT; git_object *object; git_describe_result *result = NULL; diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c index e4dfcef3e..7a6a73cb8 100644 --- a/tests/describe/describe_helpers.c +++ b/tests/describe/describe_helpers.c @@ -4,7 +4,7 @@ void assert_describe( const char *expected_output, const char *revparse_spec, git_repository *repo, - git_describe_opts *opts, + git_describe_options *opts, git_describe_format_options *fmt_opts) { git_object *object; @@ -26,7 +26,7 @@ void assert_describe( void assert_describe_workdir( const char *expected_output, git_repository *repo, - git_describe_opts *opts, + git_describe_options *opts, git_describe_format_options *fmt_opts) { git_buf label = GIT_BUF_INIT; diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h index 5d6417261..16a0638e3 100644 --- a/tests/describe/describe_helpers.h +++ b/tests/describe/describe_helpers.h @@ -5,11 +5,11 @@ extern void assert_describe( const char *expected_output, const char *revparse_spec, git_repository *repo, - git_describe_opts *opts, + git_describe_options *opts, git_describe_format_options *fmt_opts); extern void assert_describe_workdir( const char *expected_output, git_repository *repo, - git_describe_opts *opts, + git_describe_options *opts, git_describe_format_options *fmt_opts); diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c index b470938d0..2377335a5 100644 --- a/tests/describe/t6120.c +++ b/tests/describe/t6120.c @@ -18,7 +18,7 @@ void test_describe_t6120__cleanup(void) void test_describe_t6120__default(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; assert_describe("A-*", "HEAD", repo, &opts, &fmt_opts); @@ -31,7 +31,7 @@ void test_describe_t6120__default(void) void test_describe_t6120__tags(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; @@ -45,7 +45,7 @@ void test_describe_t6120__tags(void) void test_describe_t6120__all(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_ALL; @@ -56,7 +56,7 @@ void test_describe_t6120__all(void) void test_describe_t6120__longformat(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; fmt_opts.always_use_long_format = 1; @@ -67,7 +67,7 @@ void test_describe_t6120__longformat(void) void test_describe_t6120__firstparent(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; opts.describe_strategy = GIT_DESCRIBE_TAGS; @@ -79,7 +79,7 @@ void test_describe_t6120__firstparent(void) void test_describe_t6120__workdir(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; assert_describe_workdir("A-*[0-9a-f]", repo, &opts, &fmt_opts); @@ -119,7 +119,7 @@ static void commit_and_tag( void test_describe_t6120__pattern(void) { - git_describe_opts opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; git_oid tag_id; git_object *head; From 886710b77e1ddfcff59bb6be1939752f1d56285c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 09:20:08 +0200 Subject: [PATCH 10/11] describe: make mingw happy The MinGW compiler does not like it when we declare a typedef twice. --- src/describe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/describe.c b/src/describe.c index afb2e2186..08c99a7d2 100644 --- a/src/describe.c +++ b/src/describe.c @@ -166,7 +166,7 @@ cleanup: return error; } -typedef struct git_describe_result { +struct git_describe_result { int dirty; int exact_match; int fallback_to_id; @@ -174,7 +174,7 @@ typedef struct git_describe_result { git_repository *repo; struct commit_name *name; struct possible_tag *tag; -} git_describe_result; +}; struct get_name_data { From a3b9270dcfe61ce3429f82b514b302667d05bfc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Sep 2014 09:32:24 +0200 Subject: [PATCH 11/11] describe: document the API --- include/git2/describe.h | 74 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/include/git2/describe.h b/include/git2/describe.h index bdbdfcfff..66b88c4fa 100644 --- a/include/git2/describe.h +++ b/include/git2/describe.h @@ -20,6 +20,13 @@ */ GIT_BEGIN_DECL +/** + * Reference lookup strategy + * + * These behave like the --tags and --all optios to git-describe, + * namely they say to look for any reference in either refs/tags/ or + * refs/ respectively. + */ typedef enum { GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, @@ -29,8 +36,8 @@ typedef enum { /** * Describe options structure * - * Zero out for defaults. Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to - * correctly set the `version` field. E.g. + * Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to correctly set + * the `version` field. E.g. * * git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; */ @@ -40,7 +47,17 @@ typedef struct git_describe_options { unsigned int max_candidates_tags; /** default: 10 */ unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */ const char *pattern; + /** + * When calculating the distance from the matching tag or + * reference, only walk down the first-parent ancestry. + */ int only_follow_first_parent; + /** + * If no matching tag or reference is found, the describe + * operation would normally fail. If this option is set, it + * will instead fall back to showing the full id of the + * commit. + */ int show_commit_oid_as_fallback; } git_describe_options; @@ -55,12 +72,28 @@ typedef struct git_describe_options { GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version); +/** + * Options for formatting the describe string + */ typedef struct { unsigned int version; + /** + * Size of the abbreviated commit id to use. This value is the + * lower bound for the length of the abbreviated string. The + * default is 7. + */ unsigned int abbreviated_size; + /** + * Set to use the long format even when a shorter name could be used. + */ int always_use_long_format; + + /** + * If the workdir is dirty and this is set, this string will + * be appended to the description string. + */ char *dirty_suffix; } git_describe_format_options; @@ -74,18 +107,53 @@ GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *op typedef struct git_describe_result git_describe_result; +/** + * Describe a commit + * + * Perform the describe operation on the given committish object. + * + * @param result pointer to store the result. You must free this once + * you're done with it. + * @param committish a committish to describe + * @param opts the lookup options + */ GIT_EXTERN(int) git_describe_commit( git_describe_result **result, git_object *committish, git_describe_options *opts); +/** + * Describe a commit + * + * Perform the describe operation on the current commit and the + * worktree. After peforming describe on HEAD, a status is run and the + * description is considered to be dirty if there are. + * + * @param result pointer to store the result. You must free this once + * you're done with it. + * @param repo the repository in which to perform the describe + * @param opts the lookup options + */ GIT_EXTERN(int) git_describe_workdir( git_describe_result **out, git_repository *repo, git_describe_options *opts); -GIT_EXTERN(int) git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); +/** + * Print the describe result to a buffer + * + * @param result the result from `git_describe_commit()` or + * `git_describe_workdir()`. + * @param opt the formatting options + */ +GIT_EXTERN(int) git_describe_format( + git_buf *out, + const git_describe_result *result, + const git_describe_format_options *opts); +/** + * Free the describe result. + */ GIT_EXTERN(void) git_describe_result_free(git_describe_result *result); /** @} */