rugged 0.1.2 → 0.16.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (277) hide show
  1. data/README.md +25 -36
  2. data/Rakefile +20 -98
  3. data/ext/rugged/extconf.rb +44 -4
  4. data/ext/rugged/remote.c +215 -0
  5. data/ext/rugged/rugged.c +171 -0
  6. data/ext/rugged/rugged.h +126 -0
  7. data/ext/rugged/rugged_blob.c +99 -0
  8. data/ext/rugged/rugged_commit.c +224 -0
  9. data/ext/rugged/rugged_config.c +238 -0
  10. data/ext/rugged/rugged_index.c +440 -0
  11. data/ext/rugged/rugged_object.c +203 -0
  12. data/ext/rugged/rugged_reference.c +401 -0
  13. data/ext/rugged/rugged_repo.c +482 -0
  14. data/ext/rugged/rugged_revwalk.c +138 -0
  15. data/ext/rugged/rugged_signature.c +80 -0
  16. data/ext/rugged/rugged_tag.c +216 -0
  17. data/ext/rugged/rugged_tree.c +322 -0
  18. data/ext/rugged/vendor/libgit2-dist.tar.gz +0 -0
  19. data/ext/rugged/vendor/libgit2-dist/deps/http-parser/http_parser.c +1778 -0
  20. data/ext/rugged/vendor/libgit2-dist/deps/http-parser/http_parser.h +267 -0
  21. data/ext/rugged/vendor/libgit2-dist/deps/zlib/adler32.c +169 -0
  22. data/ext/rugged/vendor/libgit2-dist/deps/zlib/crc32.c +442 -0
  23. data/ext/rugged/vendor/libgit2-dist/deps/zlib/crc32.h +441 -0
  24. data/ext/rugged/vendor/libgit2-dist/deps/zlib/deflate.c +1834 -0
  25. data/ext/rugged/vendor/libgit2-dist/deps/zlib/deflate.h +342 -0
  26. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffast.c +340 -0
  27. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffast.h +11 -0
  28. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffixed.h +94 -0
  29. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inflate.c +1480 -0
  30. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inflate.h +122 -0
  31. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inftrees.c +330 -0
  32. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inftrees.h +62 -0
  33. data/ext/rugged/vendor/libgit2-dist/deps/zlib/trees.c +1244 -0
  34. data/ext/rugged/vendor/libgit2-dist/deps/zlib/trees.h +128 -0
  35. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zconf.h +54 -0
  36. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zlib.h +1613 -0
  37. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zutil.c +318 -0
  38. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zutil.h +274 -0
  39. data/ext/rugged/vendor/libgit2-dist/examples/general.c +451 -0
  40. data/ext/rugged/vendor/libgit2-dist/examples/network/common.h +14 -0
  41. data/ext/rugged/vendor/libgit2-dist/examples/network/fetch.c +97 -0
  42. data/ext/rugged/vendor/libgit2-dist/examples/network/git2.c +58 -0
  43. data/ext/rugged/vendor/libgit2-dist/examples/network/index-pack.c +47 -0
  44. data/ext/rugged/vendor/libgit2-dist/examples/network/ls-remote.c +76 -0
  45. data/ext/rugged/vendor/libgit2-dist/examples/showindex.c +43 -0
  46. data/ext/rugged/vendor/libgit2-dist/include/git2.h +44 -0
  47. data/ext/rugged/vendor/libgit2-dist/include/git2/blob.h +120 -0
  48. data/ext/rugged/vendor/libgit2-dist/include/git2/branch.h +15 -0
  49. data/ext/rugged/vendor/libgit2-dist/include/git2/commit.h +263 -0
  50. data/ext/rugged/vendor/libgit2-dist/include/git2/common.h +103 -0
  51. data/ext/rugged/vendor/libgit2-dist/include/git2/config.h +278 -0
  52. data/ext/rugged/vendor/libgit2-dist/include/git2/errors.h +144 -0
  53. data/ext/rugged/vendor/libgit2-dist/include/git2/index.h +306 -0
  54. data/ext/rugged/vendor/libgit2-dist/include/git2/indexer.h +76 -0
  55. data/ext/rugged/vendor/libgit2-dist/include/git2/inttypes.h +305 -0
  56. data/ext/rugged/vendor/libgit2-dist/include/git2/net.h +51 -0
  57. data/ext/rugged/vendor/libgit2-dist/include/git2/object.h +173 -0
  58. data/ext/rugged/vendor/libgit2-dist/include/git2/odb.h +331 -0
  59. data/ext/rugged/vendor/libgit2-dist/include/git2/odb_backend.h +100 -0
  60. data/ext/rugged/vendor/libgit2-dist/include/git2/oid.h +218 -0
  61. data/ext/rugged/vendor/libgit2-dist/include/git2/reflog.h +128 -0
  62. data/ext/rugged/vendor/libgit2-dist/include/git2/refs.h +309 -0
  63. data/ext/rugged/vendor/libgit2-dist/include/git2/refspec.h +60 -0
  64. data/ext/rugged/vendor/libgit2-dist/include/git2/remote.h +176 -0
  65. data/ext/rugged/vendor/libgit2-dist/include/git2/repository.h +290 -0
  66. data/ext/rugged/vendor/libgit2-dist/include/git2/revwalk.h +169 -0
  67. data/ext/rugged/vendor/libgit2-dist/include/git2/signature.h +65 -0
  68. data/ext/rugged/vendor/libgit2-dist/include/git2/status.h +63 -0
  69. data/ext/rugged/vendor/libgit2-dist/include/git2/stdint.h +247 -0
  70. data/ext/rugged/vendor/libgit2-dist/include/git2/tag.h +279 -0
  71. data/ext/rugged/vendor/libgit2-dist/include/git2/threads.h +48 -0
  72. data/ext/rugged/vendor/libgit2-dist/include/git2/tree.h +318 -0
  73. data/ext/rugged/vendor/libgit2-dist/include/git2/types.h +169 -0
  74. data/ext/rugged/vendor/libgit2-dist/include/git2/version.h +15 -0
  75. data/ext/rugged/vendor/libgit2-dist/include/git2/windows.h +59 -0
  76. data/ext/rugged/vendor/libgit2-dist/include/git2/zlib.h +40 -0
  77. data/ext/rugged/vendor/libgit2-dist/src/blob.c +135 -0
  78. data/ext/rugged/vendor/libgit2-dist/src/blob.h +23 -0
  79. data/ext/rugged/vendor/libgit2-dist/src/bswap.h +97 -0
  80. data/ext/rugged/vendor/libgit2-dist/src/buffer.c +113 -0
  81. data/ext/rugged/vendor/libgit2-dist/src/buffer.h +32 -0
  82. data/ext/rugged/vendor/libgit2-dist/src/cache.c +111 -0
  83. data/ext/rugged/vendor/libgit2-dist/src/cache.h +64 -0
  84. data/ext/rugged/vendor/libgit2-dist/src/cc-compat.h +67 -0
  85. data/ext/rugged/vendor/libgit2-dist/src/commit.c +299 -0
  86. data/ext/rugged/vendor/libgit2-dist/src/commit.h +34 -0
  87. data/ext/rugged/vendor/libgit2-dist/src/common.h +64 -0
  88. data/ext/rugged/vendor/libgit2-dist/src/config.c +418 -0
  89. data/ext/rugged/vendor/libgit2-dist/src/config.h +24 -0
  90. data/ext/rugged/vendor/libgit2-dist/src/config_file.c +1210 -0
  91. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.c +115 -0
  92. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.h +33 -0
  93. data/ext/rugged/vendor/libgit2-dist/src/dir.h +47 -0
  94. data/ext/rugged/vendor/libgit2-dist/src/errors.c +104 -0
  95. data/ext/rugged/vendor/libgit2-dist/src/fetch.c +172 -0
  96. data/ext/rugged/vendor/libgit2-dist/src/fetch.h +18 -0
  97. data/ext/rugged/vendor/libgit2-dist/src/filebuf.c +400 -0
  98. data/ext/rugged/vendor/libgit2-dist/src/filebuf.h +72 -0
  99. data/ext/rugged/vendor/libgit2-dist/src/fileops.c +358 -0
  100. data/ext/rugged/vendor/libgit2-dist/src/fileops.h +151 -0
  101. data/ext/rugged/vendor/libgit2-dist/src/global.c +134 -0
  102. data/ext/rugged/vendor/libgit2-dist/src/global.h +24 -0
  103. data/ext/rugged/vendor/libgit2-dist/src/hash.c +74 -0
  104. data/ext/rugged/vendor/libgit2-dist/src/hash.h +29 -0
  105. data/ext/rugged/vendor/libgit2-dist/src/hashtable.c +243 -0
  106. data/ext/rugged/vendor/libgit2-dist/src/hashtable.h +80 -0
  107. data/ext/rugged/vendor/libgit2-dist/src/index.c +918 -0
  108. data/ext/rugged/vendor/libgit2-dist/src/index.h +34 -0
  109. data/ext/rugged/vendor/libgit2-dist/src/indexer.c +401 -0
  110. data/ext/rugged/vendor/libgit2-dist/src/map.h +37 -0
  111. data/ext/rugged/vendor/libgit2-dist/src/mwindow.c +272 -0
  112. data/ext/rugged/vendor/libgit2-dist/src/mwindow.h +45 -0
  113. data/ext/rugged/vendor/libgit2-dist/src/netops.c +198 -0
  114. data/ext/rugged/vendor/libgit2-dist/src/netops.h +36 -0
  115. data/ext/rugged/vendor/libgit2-dist/src/object.c +295 -0
  116. data/ext/rugged/vendor/libgit2-dist/src/odb.c +672 -0
  117. data/ext/rugged/vendor/libgit2-dist/src/odb.h +43 -0
  118. data/ext/rugged/vendor/libgit2-dist/src/odb_loose.c +855 -0
  119. data/ext/rugged/vendor/libgit2-dist/src/odb_pack.c +485 -0
  120. data/ext/rugged/vendor/libgit2-dist/src/oid.c +388 -0
  121. data/ext/rugged/vendor/libgit2-dist/src/pack.c +788 -0
  122. data/ext/rugged/vendor/libgit2-dist/src/pack.h +99 -0
  123. data/ext/rugged/vendor/libgit2-dist/src/path.c +270 -0
  124. data/ext/rugged/vendor/libgit2-dist/src/path.h +84 -0
  125. data/ext/rugged/vendor/libgit2-dist/src/pkt.c +426 -0
  126. data/ext/rugged/vendor/libgit2-dist/src/pkt.h +77 -0
  127. data/ext/rugged/vendor/libgit2-dist/src/posix.c +94 -0
  128. data/ext/rugged/vendor/libgit2-dist/src/posix.h +69 -0
  129. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.c +70 -0
  130. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.h +26 -0
  131. data/ext/rugged/vendor/libgit2-dist/src/pqueue.c +141 -0
  132. data/ext/rugged/vendor/libgit2-dist/src/pqueue.h +81 -0
  133. data/ext/rugged/vendor/libgit2-dist/src/protocol.c +50 -0
  134. data/ext/rugged/vendor/libgit2-dist/src/protocol.h +23 -0
  135. data/ext/rugged/vendor/libgit2-dist/src/reflog.c +318 -0
  136. data/ext/rugged/vendor/libgit2-dist/src/reflog.h +34 -0
  137. data/ext/rugged/vendor/libgit2-dist/src/refs.c +1693 -0
  138. data/ext/rugged/vendor/libgit2-dist/src/refs.h +58 -0
  139. data/ext/rugged/vendor/libgit2-dist/src/refspec.c +95 -0
  140. data/ext/rugged/vendor/libgit2-dist/src/refspec.h +23 -0
  141. data/ext/rugged/vendor/libgit2-dist/src/remote.c +339 -0
  142. data/ext/rugged/vendor/libgit2-dist/src/remote.h +25 -0
  143. data/ext/rugged/vendor/libgit2-dist/src/repository.c +849 -0
  144. data/ext/rugged/vendor/libgit2-dist/src/repository.h +60 -0
  145. data/ext/rugged/vendor/libgit2-dist/src/revwalk.c +569 -0
  146. data/ext/rugged/vendor/libgit2-dist/src/sha1.c +280 -0
  147. data/ext/rugged/vendor/libgit2-dist/src/sha1.h +21 -0
  148. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.c +177 -0
  149. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.h +18 -0
  150. data/ext/rugged/vendor/libgit2-dist/src/signature.c +335 -0
  151. data/ext/rugged/vendor/libgit2-dist/src/signature.h +18 -0
  152. data/ext/rugged/vendor/libgit2-dist/src/status.c +696 -0
  153. data/ext/rugged/vendor/libgit2-dist/src/tag.c +446 -0
  154. data/ext/rugged/vendor/libgit2-dist/src/tag.h +28 -0
  155. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.c +55 -0
  156. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.h +108 -0
  157. data/ext/rugged/vendor/libgit2-dist/src/transport.c +85 -0
  158. data/ext/rugged/vendor/libgit2-dist/src/transport.h +110 -0
  159. data/ext/rugged/vendor/libgit2-dist/src/transports/git.c +502 -0
  160. data/ext/rugged/vendor/libgit2-dist/src/transports/http.c +756 -0
  161. data/ext/rugged/vendor/libgit2-dist/src/transports/local.c +235 -0
  162. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.c +201 -0
  163. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.h +31 -0
  164. data/ext/rugged/vendor/libgit2-dist/src/tree.c +758 -0
  165. data/ext/rugged/vendor/libgit2-dist/src/tree.h +37 -0
  166. data/ext/rugged/vendor/libgit2-dist/src/tsort.c +365 -0
  167. data/ext/rugged/vendor/libgit2-dist/src/unix/map.c +70 -0
  168. data/ext/rugged/vendor/libgit2-dist/src/unix/posix.h +25 -0
  169. data/ext/rugged/vendor/libgit2-dist/src/util.c +381 -0
  170. data/ext/rugged/vendor/libgit2-dist/src/util.h +137 -0
  171. data/ext/rugged/vendor/libgit2-dist/src/vector.c +174 -0
  172. data/ext/rugged/vendor/libgit2-dist/src/vector.h +45 -0
  173. data/ext/rugged/vendor/libgit2-dist/src/win32/dir.c +115 -0
  174. data/ext/rugged/vendor/libgit2-dist/src/win32/fnmatch.c +180 -0
  175. data/ext/rugged/vendor/libgit2-dist/src/win32/fnmatch.h +27 -0
  176. data/ext/rugged/vendor/libgit2-dist/src/win32/map.c +131 -0
  177. data/ext/rugged/vendor/libgit2-dist/src/win32/mingw-compat.h +24 -0
  178. data/ext/rugged/vendor/libgit2-dist/src/win32/msvc-compat.h +38 -0
  179. data/ext/rugged/vendor/libgit2-dist/src/win32/posix.h +53 -0
  180. data/ext/rugged/vendor/libgit2-dist/src/win32/posix_w32.c +404 -0
  181. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.c +65 -0
  182. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.h +40 -0
  183. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.c +88 -0
  184. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.h +17 -0
  185. data/ext/rugged/vendor/libgit2-dist/tests-clay/buf/basic.c +29 -0
  186. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay.h +187 -0
  187. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_libgit2.h +28 -0
  188. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_main.c +1073 -0
  189. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/add.c +37 -0
  190. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/new.c +36 -0
  191. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/read.c +209 -0
  192. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/stress.c +39 -0
  193. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/write.c +77 -0
  194. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/dirent.c +222 -0
  195. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/filebuf.c +106 -0
  196. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/oid.c +18 -0
  197. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/path.c +139 -0
  198. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/rmdir.c +50 -0
  199. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/string.c +28 -0
  200. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/strtol.c +37 -0
  201. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/vector.c +66 -0
  202. data/ext/rugged/vendor/libgit2-dist/tests-clay/index/rename.c +60 -0
  203. data/ext/rugged/vendor/libgit2-dist/tests-clay/network/remotes.c +50 -0
  204. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/chars.c +52 -0
  205. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/compare.c +124 -0
  206. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/convert.c +75 -0
  207. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/data.h +323 -0
  208. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/fromstr.c +30 -0
  209. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/hash.c +162 -0
  210. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/short.c +94 -0
  211. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/size.c +13 -0
  212. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/type2string.c +54 -0
  213. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/tree/frompath.c +75 -0
  214. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/loose.c +84 -0
  215. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/loose_data.h +522 -0
  216. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/pack_data.h +151 -0
  217. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/packed.c +78 -0
  218. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/sorting.c +71 -0
  219. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/getters.c +68 -0
  220. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/init.c +104 -0
  221. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/open.c +54 -0
  222. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/single.c +38 -0
  223. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/status_data.h +48 -0
  224. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/worktree.c +124 -0
  225. data/ext/rugged/vendor/libgit2-dist/tests/t00-core.c +628 -0
  226. data/ext/rugged/vendor/libgit2-dist/tests/t01-data.h +322 -0
  227. data/ext/rugged/vendor/libgit2-dist/tests/t01-rawobj.c +638 -0
  228. data/ext/rugged/vendor/libgit2-dist/tests/t03-data.h +344 -0
  229. data/ext/rugged/vendor/libgit2-dist/tests/t03-objwrite.c +255 -0
  230. data/ext/rugged/vendor/libgit2-dist/tests/t04-commit.c +788 -0
  231. data/ext/rugged/vendor/libgit2-dist/tests/t05-revwalk.c +140 -0
  232. data/ext/rugged/vendor/libgit2-dist/tests/t06-index.c +219 -0
  233. data/ext/rugged/vendor/libgit2-dist/tests/t07-hashtable.c +192 -0
  234. data/ext/rugged/vendor/libgit2-dist/tests/t08-tag.c +357 -0
  235. data/ext/rugged/vendor/libgit2-dist/tests/t09-tree.c +221 -0
  236. data/ext/rugged/vendor/libgit2-dist/tests/t10-refs.c +1294 -0
  237. data/ext/rugged/vendor/libgit2-dist/tests/t12-repo.c +174 -0
  238. data/ext/rugged/vendor/libgit2-dist/tests/t13-threads.c +41 -0
  239. data/ext/rugged/vendor/libgit2-dist/tests/t17-bufs.c +61 -0
  240. data/ext/rugged/vendor/libgit2-dist/tests/t18-status.c +448 -0
  241. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.c +310 -0
  242. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.h +83 -0
  243. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.c +198 -0
  244. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.h +54 -0
  245. data/ext/rugged/vendor/libgit2-dist/tests/test_main.c +89 -0
  246. data/lib/rugged.rb +4 -3
  247. data/lib/rugged/index.rb +0 -8
  248. data/lib/rugged/objects.rb +45 -0
  249. data/lib/rugged/repository.rb +29 -0
  250. data/lib/rugged/tree.rb +16 -6
  251. data/lib/rugged/version.rb +1 -1
  252. data/lib/rugged/walker.rb +5 -0
  253. data/test/blob_test.rb +18 -14
  254. data/test/commit_test.rb +28 -26
  255. data/test/coverage/HEAD.json +1 -0
  256. data/test/coverage/cover.rb +106 -0
  257. data/test/fixtures/testrepo.git/refs/heads/new_name +1 -0
  258. data/test/index_test.rb +101 -78
  259. data/test/lib_test.rb +4 -4
  260. data/test/object_test.rb +3 -3
  261. data/test/reference_test.rb +75 -0
  262. data/test/remote_test.rb +19 -0
  263. data/test/repo_pack_test.rb +4 -4
  264. data/test/repo_test.rb +44 -15
  265. data/test/tag_test.rb +7 -17
  266. data/test/test_helper.rb +24 -5
  267. data/test/tree_test.rb +30 -12
  268. data/test/walker_test.rb +40 -31
  269. metadata +273 -50
  270. data/lib/rugged/person.rb +0 -20
  271. data/lib/rugged/tree_entry.rb +0 -9
  272. data/test/fixtures/testrepo.git/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77ae +0 -0
  273. data/test/fixtures/testrepo.git/objects/2f/3321418db5b2a841375b8b70880a8ab5a4148f +0 -0
  274. data/test/fixtures/testrepo.git/objects/36/9b00a7700cca3a506d79e301d6ad8bf735d9ee +0 -3
  275. data/test/fixtures/testrepo.git/objects/3d/b1b5ceace59ff65279757003763046fd4cbbe6 +0 -0
  276. data/test/fixtures/testrepo.git/objects/4c/d1604907792e2c43e03dcec1216f99d63e68c4 +0 -3
  277. data/test/fixtures/testrepo.git/objects/e0/f46d77041c149296549b01ed4a18b02c4b7400 +0 -0
@@ -0,0 +1,50 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #include "common.h"
8
+ #include "protocol.h"
9
+ #include "pkt.h"
10
+ #include "buffer.h"
11
+
12
+ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
13
+ {
14
+ git_buf *buf = &p->buf;
15
+ git_vector *refs = p->refs;
16
+ int error;
17
+ const char *line_end, *ptr;
18
+
19
+ if (len == 0) { /* EOF */
20
+ if (buf->size != 0)
21
+ return p->error = git__throw(GIT_ERROR, "EOF and unprocessed data");
22
+ else
23
+ return 0;
24
+ }
25
+
26
+ git_buf_put(buf, data, len);
27
+ ptr = buf->ptr;
28
+ while (1) {
29
+ git_pkt *pkt;
30
+
31
+ if (buf->size == 0)
32
+ return 0;
33
+
34
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size);
35
+ if (error == GIT_ESHORTBUFFER)
36
+ return 0; /* Ask for more */
37
+ if (error < GIT_SUCCESS)
38
+ return p->error = git__rethrow(error, "Failed to parse pkt-line");
39
+
40
+ git_buf_consume(buf, line_end);
41
+ error = git_vector_insert(refs, pkt);
42
+ if (error < GIT_SUCCESS)
43
+ return p->error = git__rethrow(error, "Failed to add pkt to list");
44
+
45
+ if (pkt->type == GIT_PKT_FLUSH)
46
+ p->flush = 1;
47
+ }
48
+
49
+ return error;
50
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_protocol_h__
8
+ #define INCLUDE_protocol_h__
9
+
10
+ #include "transport.h"
11
+ #include "buffer.h"
12
+
13
+ typedef struct {
14
+ git_transport *transport;
15
+ git_vector *refs;
16
+ git_buf buf;
17
+ int error;
18
+ unsigned int flush :1;
19
+ } git_protocol;
20
+
21
+ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len);
22
+
23
+ #endif
@@ -0,0 +1,318 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #include "reflog.h"
9
+ #include "repository.h"
10
+ #include "filebuf.h"
11
+ #include "signature.h"
12
+
13
+ static int reflog_init(git_reflog **reflog, git_reference *ref)
14
+ {
15
+ git_reflog *log;
16
+
17
+ *reflog = NULL;
18
+
19
+ log = git__malloc(sizeof(git_reflog));
20
+ if (log == NULL)
21
+ return GIT_ENOMEM;
22
+
23
+ memset(log, 0x0, sizeof(git_reflog));
24
+
25
+ log->ref_name = git__strdup(ref->name);
26
+
27
+ if (git_vector_init(&log->entries, 0, NULL) < 0) {
28
+ git__free(log->ref_name);
29
+ git__free(log);
30
+ return GIT_ENOMEM;
31
+ }
32
+
33
+ *reflog = log;
34
+
35
+ return GIT_SUCCESS;
36
+ }
37
+
38
+ static int reflog_write(const char *log_path, const char *oid_old,
39
+ const char *oid_new, const git_signature *committer,
40
+ const char *msg)
41
+ {
42
+ int error;
43
+ git_buf log = GIT_BUF_INIT;
44
+ git_filebuf fbuf = GIT_FILEBUF_INIT;
45
+
46
+ assert(log_path && oid_old && oid_new && committer);
47
+
48
+ git_buf_puts(&log, oid_old);
49
+ git_buf_putc(&log, ' ');
50
+
51
+ git_buf_puts(&log, oid_new);
52
+
53
+ git_signature__writebuf(&log, " ", committer);
54
+ log.size--; /* drop LF */
55
+
56
+ if (msg) {
57
+ if (strchr(msg, '\n')) {
58
+ git_buf_free(&log);
59
+ return git__throw(GIT_ERROR, "Reflog message cannot contain newline");
60
+ }
61
+
62
+ git_buf_putc(&log, '\t');
63
+ git_buf_puts(&log, msg);
64
+ }
65
+
66
+ git_buf_putc(&log, '\n');
67
+
68
+ if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) {
69
+ git_buf_free(&log);
70
+ return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path);
71
+ }
72
+
73
+ git_filebuf_write(&fbuf, log.ptr, log.size);
74
+ error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
75
+
76
+ git_buf_free(&log);
77
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog");
78
+ }
79
+
80
+ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
81
+ {
82
+ int error = GIT_SUCCESS;
83
+ const char *ptr;
84
+ git_reflog_entry *entry;
85
+
86
+ #define seek_forward(_increase) { \
87
+ if (_increase >= buf_size) { \
88
+ if (entry->committer) \
89
+ git__free(entry->committer); \
90
+ git__free(entry); \
91
+ return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \
92
+ } \
93
+ buf += _increase; \
94
+ buf_size -= _increase; \
95
+ }
96
+
97
+ while (buf_size > GIT_REFLOG_SIZE_MIN) {
98
+ entry = git__malloc(sizeof(git_reflog_entry));
99
+ if (entry == NULL)
100
+ return GIT_ENOMEM;
101
+ entry->committer = NULL;
102
+
103
+ if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
104
+ git__free(entry);
105
+ return GIT_ERROR;
106
+ }
107
+ seek_forward(GIT_OID_HEXSZ + 1);
108
+
109
+ if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
110
+ git__free(entry);
111
+ return GIT_ERROR;
112
+ }
113
+ seek_forward(GIT_OID_HEXSZ + 1);
114
+
115
+ ptr = buf;
116
+
117
+ /* Seek forward to the end of the signature. */
118
+ while (*buf && *buf != '\t' && *buf != '\n')
119
+ seek_forward(1);
120
+
121
+ entry->committer = git__malloc(sizeof(git_signature));
122
+ if (entry->committer == NULL) {
123
+ git__free(entry);
124
+ return GIT_ENOMEM;
125
+ }
126
+
127
+ if ((error = git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf)) < GIT_SUCCESS) {
128
+ git__free(entry->committer);
129
+ git__free(entry);
130
+ return git__rethrow(error, "Failed to parse reflog. Could not parse signature");
131
+ }
132
+
133
+ if (*buf == '\t') {
134
+ /* We got a message. Read everything till we reach LF. */
135
+ seek_forward(1);
136
+ ptr = buf;
137
+
138
+ while (*buf && *buf != '\n')
139
+ seek_forward(1);
140
+
141
+ entry->msg = git__strndup(ptr, buf - ptr);
142
+ } else
143
+ entry->msg = NULL;
144
+
145
+ while (*buf && *buf == '\n' && buf_size > 1)
146
+ seek_forward(1);
147
+
148
+ if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS)
149
+ return git__rethrow(error, "Failed to parse reflog. Could not add new entry");
150
+ }
151
+
152
+ #undef seek_forward
153
+
154
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog");
155
+ }
156
+
157
+ void git_reflog_free(git_reflog *reflog)
158
+ {
159
+ unsigned int i;
160
+ git_reflog_entry *entry;
161
+
162
+ for (i=0; i < reflog->entries.length; i++) {
163
+ entry = git_vector_get(&reflog->entries, i);
164
+
165
+ git_signature_free(entry->committer);
166
+
167
+ git__free(entry->msg);
168
+ git__free(entry);
169
+ }
170
+
171
+ git_vector_free(&reflog->entries);
172
+ git__free(reflog->ref_name);
173
+ git__free(reflog);
174
+ }
175
+
176
+ int git_reflog_read(git_reflog **reflog, git_reference *ref)
177
+ {
178
+ int error;
179
+ char log_path[GIT_PATH_MAX];
180
+ git_fbuffer log_file = GIT_FBUFFER_INIT;
181
+ git_reflog *log = NULL;
182
+
183
+ *reflog = NULL;
184
+
185
+ if ((error = reflog_init(&log, ref)) < GIT_SUCCESS)
186
+ return git__rethrow(error, "Failed to read reflog. Cannot init reflog");
187
+
188
+ git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
189
+
190
+ if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) {
191
+ git_reflog_free(log);
192
+ return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path);
193
+ }
194
+
195
+ error = reflog_parse(log, log_file.data, log_file.len);
196
+
197
+ git_futils_freebuffer(&log_file);
198
+
199
+ if (error == GIT_SUCCESS)
200
+ *reflog = log;
201
+ else
202
+ git_reflog_free(log);
203
+
204
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog");
205
+ }
206
+
207
+ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
208
+ const git_signature *committer, const char *msg)
209
+ {
210
+ int error;
211
+ char old[GIT_OID_HEXSZ+1];
212
+ char new[GIT_OID_HEXSZ+1];
213
+ char log_path[GIT_PATH_MAX];
214
+ git_reference *r;
215
+ const git_oid *oid;
216
+
217
+ if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS)
218
+ return git__rethrow(error,
219
+ "Failed to write reflog. Cannot resolve reference `%s`", ref->name);
220
+
221
+ oid = git_reference_oid(r);
222
+ if (oid == NULL) {
223
+ git_reference_free(r);
224
+ return git__throw(GIT_ERROR,
225
+ "Failed to write reflog. Cannot resolve reference `%s`", r->name);
226
+ }
227
+
228
+ git_oid_to_string(new, GIT_OID_HEXSZ+1, oid);
229
+
230
+ git_path_join_n(log_path, 3,
231
+ ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
232
+
233
+ git_reference_free(r);
234
+
235
+ if (git_futils_exists(log_path)) {
236
+ error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE);
237
+ if (error < GIT_SUCCESS)
238
+ return git__rethrow(error,
239
+ "Failed to write reflog. Cannot create reflog directory");
240
+
241
+ } else if (git_futils_isfile(log_path)) {
242
+ return git__throw(GIT_ERROR,
243
+ "Failed to write reflog. `%s` is directory", log_path);
244
+
245
+ } else if (oid_old == NULL) {
246
+ return git__throw(GIT_ERROR,
247
+ "Failed to write reflog. Old OID cannot be NULL for existing reference");
248
+ }
249
+
250
+ if (oid_old)
251
+ git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old);
252
+ else
253
+ p_snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0);
254
+
255
+ return reflog_write(log_path, old, new, committer, msg);
256
+ }
257
+
258
+ int git_reflog_rename(git_reference *ref, const char *new_name)
259
+ {
260
+ char old_path[GIT_PATH_MAX];
261
+ char new_path[GIT_PATH_MAX];
262
+
263
+ git_path_join_n(old_path, 3, ref->owner->path_repository,
264
+ GIT_REFLOG_DIR, ref->name);
265
+ git_path_join_n(new_path, 3, ref->owner->path_repository,
266
+ GIT_REFLOG_DIR, new_name);
267
+
268
+ return p_rename(old_path, new_path);
269
+ }
270
+
271
+ int git_reflog_delete(git_reference *ref)
272
+ {
273
+ char path[GIT_PATH_MAX];
274
+
275
+ git_path_join_n(path, 3, ref->owner->path_repository,
276
+ GIT_REFLOG_DIR, ref->name);
277
+
278
+ if (git_futils_exists(path))
279
+ return GIT_SUCCESS;
280
+
281
+ return p_unlink(path);
282
+ }
283
+
284
+ unsigned int git_reflog_entrycount(git_reflog *reflog)
285
+ {
286
+ assert(reflog);
287
+ return reflog->entries.length;
288
+ }
289
+
290
+ const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx)
291
+ {
292
+ assert(reflog);
293
+ return git_vector_get(&reflog->entries, idx);
294
+ }
295
+
296
+ const git_oid * git_reflog_entry_oidold(const git_reflog_entry *entry)
297
+ {
298
+ assert(entry);
299
+ return &entry->oid_old;
300
+ }
301
+
302
+ const git_oid * git_reflog_entry_oidnew(const git_reflog_entry *entry)
303
+ {
304
+ assert(entry);
305
+ return &entry->oid_cur;
306
+ }
307
+
308
+ git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
309
+ {
310
+ assert(entry);
311
+ return entry->committer;
312
+ }
313
+
314
+ char * git_reflog_entry_msg(const git_reflog_entry *entry)
315
+ {
316
+ assert(entry);
317
+ return entry->msg;
318
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_reflog_h__
8
+ #define INCLUDE_reflog_h__
9
+
10
+ #include "common.h"
11
+ #include "git2/reflog.h"
12
+ #include "vector.h"
13
+
14
+ #define GIT_REFLOG_DIR "logs/"
15
+ #define GIT_REFLOG_DIR_MODE 0777
16
+ #define GIT_REFLOG_FILE_MODE 0666
17
+
18
+ #define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17)
19
+
20
+ struct git_reflog_entry {
21
+ git_oid oid_old;
22
+ git_oid oid_cur;
23
+
24
+ git_signature *committer;
25
+
26
+ char *msg;
27
+ };
28
+
29
+ struct git_reflog {
30
+ char *ref_name;
31
+ git_vector entries;
32
+ };
33
+
34
+ #endif /* INCLUDE_reflog_h__ */
@@ -0,0 +1,1693 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #include "refs.h"
9
+ #include "hash.h"
10
+ #include "repository.h"
11
+ #include "fileops.h"
12
+ #include "pack.h"
13
+ #include "reflog.h"
14
+
15
+ #include <git2/tag.h>
16
+ #include <git2/object.h>
17
+
18
+ #define MAX_NESTING_LEVEL 5
19
+
20
+ enum {
21
+ GIT_PACKREF_HAS_PEEL = 1,
22
+ GIT_PACKREF_WAS_LOOSE = 2
23
+ };
24
+
25
+ struct packref {
26
+ git_oid oid;
27
+ git_oid peel;
28
+ char flags;
29
+ char name[GIT_FLEX_ARRAY];
30
+ };
31
+
32
+ static const int default_table_size = 32;
33
+
34
+ static uint32_t reftable_hash(const void *key, int hash_id)
35
+ {
36
+ static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
37
+ 2147483647,
38
+ 0x5d20bb23,
39
+ 0x7daaab3c
40
+ };
41
+
42
+ return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
43
+ }
44
+
45
+ static int reference_read(
46
+ git_fbuffer *file_content,
47
+ time_t *mtime,
48
+ const char *repo_path,
49
+ const char *ref_name,
50
+ int *updated);
51
+
52
+ /* loose refs */
53
+ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
54
+ static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content);
55
+ static int loose_lookup(git_reference *ref);
56
+ static int loose_lookup_to_packfile(struct packref **ref_out,
57
+ git_repository *repo, const char *name);
58
+ static int loose_write(git_reference *ref);
59
+
60
+ /* packed refs */
61
+ static int packed_parse_peel(struct packref *tag_ref,
62
+ const char **buffer_out, const char *buffer_end);
63
+ static int packed_parse_oid(struct packref **ref_out,
64
+ const char **buffer_out, const char *buffer_end);
65
+ static int packed_load(git_repository *repo);
66
+ static int packed_loadloose(git_repository *repository);
67
+ static int packed_write_ref(struct packref *ref, git_filebuf *file);
68
+ static int packed_find_peel(git_repository *repo, struct packref *ref);
69
+ static int packed_remove_loose(git_repository *repo, git_vector *packing_list);
70
+ static int packed_sort(const void *a, const void *b);
71
+ static int packed_lookup(git_reference *ref);
72
+ static int packed_write(git_repository *repo);
73
+
74
+ /* internal helpers */
75
+ static int reference_available(git_repository *repo,
76
+ const char *ref, const char *old_ref);
77
+ static int reference_delete(git_reference *ref);
78
+ static int reference_lookup(git_reference *ref);
79
+
80
+ /* name normalization */
81
+ static int normalize_name(char *buffer_out, size_t out_size,
82
+ const char *name, int is_oid_ref);
83
+
84
+
85
+ void git_reference_free(git_reference *reference)
86
+ {
87
+ if (reference == NULL)
88
+ return;
89
+
90
+ git__free(reference->name);
91
+
92
+ if (reference->flags & GIT_REF_SYMBOLIC)
93
+ git__free(reference->target.symbolic);
94
+
95
+ git__free(reference);
96
+ }
97
+
98
+ static int reference_create(
99
+ git_reference **ref_out,
100
+ git_repository *repo,
101
+ const char *name)
102
+ {
103
+ git_reference *reference = NULL;
104
+
105
+ assert(ref_out && repo && name);
106
+
107
+ reference = git__malloc(sizeof(git_reference));
108
+ if (reference == NULL)
109
+ return GIT_ENOMEM;
110
+
111
+ memset(reference, 0x0, sizeof(git_reference));
112
+ reference->owner = repo;
113
+
114
+ reference->name = git__strdup(name);
115
+ if (reference->name == NULL) {
116
+ free(reference);
117
+ return GIT_ENOMEM;
118
+ }
119
+
120
+ *ref_out = reference;
121
+ return GIT_SUCCESS;
122
+ }
123
+
124
+ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
125
+ {
126
+ char path[GIT_PATH_MAX];
127
+
128
+ assert(file_content && repo_path && ref_name);
129
+
130
+ /* Determine the full path of the file */
131
+ git_path_join(path, repo_path, ref_name);
132
+
133
+ return git_futils_readbuffer_updated(file_content, path, mtime, updated);
134
+ }
135
+
136
+ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
137
+ {
138
+ const unsigned int header_len = strlen(GIT_SYMREF);
139
+ const char *refname_start;
140
+ char *eol;
141
+
142
+ refname_start = (const char *)file_content->data;
143
+
144
+ if (file_content->len < (header_len + 1))
145
+ return git__throw(GIT_EOBJCORRUPTED,
146
+ "Failed to parse loose reference. Object too short");
147
+
148
+ /*
149
+ * Assume we have already checked for the header
150
+ * before calling this function
151
+ */
152
+
153
+ refname_start += header_len;
154
+
155
+ ref->target.symbolic = git__strdup(refname_start);
156
+ if (ref->target.symbolic == NULL)
157
+ return GIT_ENOMEM;
158
+
159
+ /* remove newline at the end of file */
160
+ eol = strchr(ref->target.symbolic, '\n');
161
+ if (eol == NULL)
162
+ return git__throw(GIT_EOBJCORRUPTED,
163
+ "Failed to parse loose reference. Missing EOL");
164
+
165
+ *eol = '\0';
166
+ if (eol[-1] == '\r')
167
+ eol[-1] = '\0';
168
+
169
+ return GIT_SUCCESS;
170
+ }
171
+
172
+ static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
173
+ {
174
+ int error;
175
+ char *buffer;
176
+
177
+ buffer = (char *)file_content->data;
178
+
179
+ /* File format: 40 chars (OID) + newline */
180
+ if (file_content->len < GIT_OID_HEXSZ + 1)
181
+ return git__throw(GIT_EOBJCORRUPTED,
182
+ "Failed to parse loose reference. Reference too short");
183
+
184
+ if ((error = git_oid_fromstr(oid, buffer)) < GIT_SUCCESS)
185
+ return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
186
+
187
+ buffer = buffer + GIT_OID_HEXSZ;
188
+ if (*buffer == '\r')
189
+ buffer++;
190
+
191
+ if (*buffer != '\n')
192
+ return git__throw(GIT_EOBJCORRUPTED,
193
+ "Failed to parse loose reference. Missing EOL");
194
+
195
+ return GIT_SUCCESS;
196
+ }
197
+
198
+ static git_rtype loose_guess_rtype(const char *full_path)
199
+ {
200
+ git_fbuffer ref_file = GIT_FBUFFER_INIT;
201
+ git_rtype type;
202
+
203
+ type = GIT_REF_INVALID;
204
+
205
+ if (git_futils_readbuffer(&ref_file, full_path) == GIT_SUCCESS) {
206
+ if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
207
+ type = GIT_REF_SYMBOLIC;
208
+ else
209
+ type = GIT_REF_OID;
210
+ }
211
+
212
+ git_futils_freebuffer(&ref_file);
213
+ return type;
214
+ }
215
+
216
+ static int loose_lookup(git_reference *ref)
217
+ {
218
+ int error = GIT_SUCCESS, updated;
219
+ git_fbuffer ref_file = GIT_FBUFFER_INIT;
220
+
221
+ if (reference_read(&ref_file, &ref->mtime,
222
+ ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
223
+ return git__throw(GIT_ENOTFOUND, "Failed to lookup loose reference");
224
+
225
+ if (!updated)
226
+ return GIT_SUCCESS;
227
+
228
+ if (ref->flags & GIT_REF_SYMBOLIC)
229
+ free(ref->target.symbolic);
230
+
231
+ ref->flags = 0;
232
+
233
+ if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
234
+ ref->flags |= GIT_REF_SYMBOLIC;
235
+ error = loose_parse_symbolic(ref, &ref_file);
236
+ } else {
237
+ ref->flags |= GIT_REF_OID;
238
+ error = loose_parse_oid(&ref->target.oid, &ref_file);
239
+ }
240
+
241
+ git_futils_freebuffer(&ref_file);
242
+
243
+ if (error < GIT_SUCCESS)
244
+ return git__rethrow(error, "Failed to lookup loose reference");
245
+
246
+ return GIT_SUCCESS;
247
+ }
248
+
249
+ static int loose_lookup_to_packfile(
250
+ struct packref **ref_out,
251
+ git_repository *repo,
252
+ const char *name)
253
+ {
254
+ int error = GIT_SUCCESS;
255
+ git_fbuffer ref_file = GIT_FBUFFER_INIT;
256
+ struct packref *ref = NULL;
257
+ size_t name_len;
258
+
259
+ *ref_out = NULL;
260
+
261
+ error = reference_read(&ref_file, NULL, repo->path_repository, name, NULL);
262
+ if (error < GIT_SUCCESS)
263
+ goto cleanup;
264
+
265
+ name_len = strlen(name);
266
+ ref = git__malloc(sizeof(struct packref) + name_len + 1);
267
+
268
+ memcpy(ref->name, name, name_len);
269
+ ref->name[name_len] = 0;
270
+
271
+ error = loose_parse_oid(&ref->oid, &ref_file);
272
+ if (error < GIT_SUCCESS)
273
+ goto cleanup;
274
+
275
+ ref->flags = GIT_PACKREF_WAS_LOOSE;
276
+
277
+ *ref_out = ref;
278
+ git_futils_freebuffer(&ref_file);
279
+ return GIT_SUCCESS;
280
+
281
+ cleanup:
282
+ git_futils_freebuffer(&ref_file);
283
+ free(ref);
284
+ return git__rethrow(error, "Failed to lookup loose reference");
285
+ }
286
+
287
+ static int loose_write(git_reference *ref)
288
+ {
289
+ git_filebuf file = GIT_FILEBUF_INIT;
290
+ char ref_path[GIT_PATH_MAX];
291
+ int error;
292
+ struct stat st;
293
+
294
+ git_path_join(ref_path, ref->owner->path_repository, ref->name);
295
+
296
+ if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS)
297
+ return git__rethrow(error, "Failed to write loose reference");
298
+
299
+ if (ref->flags & GIT_REF_OID) {
300
+ char oid[GIT_OID_HEXSZ + 1];
301
+
302
+ git_oid_fmt(oid, &ref->target.oid);
303
+ oid[GIT_OID_HEXSZ] = '\0';
304
+
305
+ error = git_filebuf_printf(&file, "%s\n", oid);
306
+ if (error < GIT_SUCCESS)
307
+ goto unlock;
308
+
309
+ } else if (ref->flags & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */
310
+ error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
311
+ } else {
312
+ error = git__throw(GIT_EOBJCORRUPTED,
313
+ "Failed to write reference. Invalid reference type");
314
+ goto unlock;
315
+ }
316
+
317
+ if (p_stat(ref_path, &st) == GIT_SUCCESS)
318
+ ref->mtime = st.st_mtime;
319
+
320
+ return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
321
+
322
+ unlock:
323
+ git_filebuf_cleanup(&file);
324
+ return git__rethrow(error, "Failed to write loose reference");
325
+ }
326
+
327
+ static int packed_parse_peel(
328
+ struct packref *tag_ref,
329
+ const char **buffer_out,
330
+ const char *buffer_end)
331
+ {
332
+ const char *buffer = *buffer_out + 1;
333
+
334
+ assert(buffer[-1] == '^');
335
+
336
+ /* Ensure it's not the first entry of the file */
337
+ if (tag_ref == NULL)
338
+ return git__throw(GIT_EPACKEDREFSCORRUPTED,
339
+ "Failed to parse packed reference. "
340
+ "Reference is the first entry of the file");
341
+
342
+ /* Ensure reference is a tag */
343
+ if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
344
+ return git__throw(GIT_EPACKEDREFSCORRUPTED,
345
+ "Failed to parse packed reference. Reference is not a tag");
346
+
347
+ if (buffer + GIT_OID_HEXSZ >= buffer_end)
348
+ return git__throw(GIT_EPACKEDREFSCORRUPTED,
349
+ "Failed to parse packed reference. Buffer too small");
350
+
351
+ /* Is this a valid object id? */
352
+ if (git_oid_fromstr(&tag_ref->peel, buffer) < GIT_SUCCESS)
353
+ return git__throw(GIT_EPACKEDREFSCORRUPTED,
354
+ "Failed to parse packed reference. Not a valid object ID");
355
+
356
+ buffer = buffer + GIT_OID_HEXSZ;
357
+ if (*buffer == '\r')
358
+ buffer++;
359
+
360
+ if (*buffer != '\n')
361
+ return git__throw(GIT_EPACKEDREFSCORRUPTED,
362
+ "Failed to parse packed reference. Buffer not terminated correctly");
363
+
364
+ *buffer_out = buffer + 1;
365
+ return GIT_SUCCESS;
366
+ }
367
+
368
+ static int packed_parse_oid(
369
+ struct packref **ref_out,
370
+ const char **buffer_out,
371
+ const char *buffer_end)
372
+ {
373
+ struct packref *ref = NULL;
374
+
375
+ const char *buffer = *buffer_out;
376
+ const char *refname_begin, *refname_end;
377
+
378
+ int error = GIT_SUCCESS;
379
+ size_t refname_len;
380
+ git_oid id;
381
+
382
+ refname_begin = (buffer + GIT_OID_HEXSZ + 1);
383
+ if (refname_begin >= buffer_end ||
384
+ refname_begin[-1] != ' ') {
385
+ error = GIT_EPACKEDREFSCORRUPTED;
386
+ goto cleanup;
387
+ }
388
+
389
+ /* Is this a valid object id? */
390
+ if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS)
391
+ goto cleanup;
392
+
393
+ refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
394
+ if (refname_end == NULL) {
395
+ error = GIT_EPACKEDREFSCORRUPTED;
396
+ goto cleanup;
397
+ }
398
+
399
+ if (refname_end[-1] == '\r')
400
+ refname_end--;
401
+
402
+ refname_len = refname_end - refname_begin;
403
+
404
+ ref = git__malloc(sizeof(struct packref) + refname_len + 1);
405
+
406
+ memcpy(ref->name, refname_begin, refname_len);
407
+ ref->name[refname_len] = 0;
408
+
409
+ git_oid_cpy(&ref->oid, &id);
410
+
411
+ ref->flags = 0;
412
+
413
+ *ref_out = ref;
414
+ *buffer_out = refname_end + 1;
415
+
416
+ return GIT_SUCCESS;
417
+
418
+ cleanup:
419
+ free(ref);
420
+ return git__rethrow(error, "Failed to parse OID of packed reference");
421
+ }
422
+
423
+ static int packed_load(git_repository *repo)
424
+ {
425
+ int error = GIT_SUCCESS, updated;
426
+ git_fbuffer packfile = GIT_FBUFFER_INIT;
427
+ const char *buffer_start, *buffer_end;
428
+ git_refcache *ref_cache = &repo->references;
429
+
430
+ /* First we make sure we have allocated the hash table */
431
+ if (ref_cache->packfile == NULL) {
432
+ ref_cache->packfile = git_hashtable_alloc(
433
+ default_table_size,
434
+ reftable_hash,
435
+ (git_hash_keyeq_ptr)&git__strcmp_cb);
436
+
437
+ if (ref_cache->packfile == NULL) {
438
+ error = GIT_ENOMEM;
439
+ goto cleanup;
440
+ }
441
+ }
442
+
443
+ error = reference_read(&packfile, &ref_cache->packfile_time,
444
+ repo->path_repository, GIT_PACKEDREFS_FILE, &updated);
445
+
446
+ /*
447
+ * If we couldn't find the file, we need to clear the table and
448
+ * return. On any other error, we return that error. If everything
449
+ * went fine and the file wasn't updated, then there's nothing new
450
+ * for us here, so just return. Anything else means we need to
451
+ * refresh the packed refs.
452
+ */
453
+ if (error == GIT_ENOTFOUND) {
454
+ git_hashtable_clear(ref_cache->packfile);
455
+ return GIT_SUCCESS;
456
+ } else if (error < GIT_SUCCESS) {
457
+ return git__rethrow(error, "Failed to read packed refs");
458
+ } else if (!updated) {
459
+ return GIT_SUCCESS;
460
+ }
461
+
462
+ /*
463
+ * At this point, we want to refresh the packed refs. We already
464
+ * have the contents in our buffer.
465
+ */
466
+
467
+ git_hashtable_clear(ref_cache->packfile);
468
+
469
+ buffer_start = (const char *)packfile.data;
470
+ buffer_end = (const char *)(buffer_start) + packfile.len;
471
+
472
+ while (buffer_start < buffer_end && buffer_start[0] == '#') {
473
+ buffer_start = strchr(buffer_start, '\n');
474
+ if (buffer_start == NULL) {
475
+ error = GIT_EPACKEDREFSCORRUPTED;
476
+ goto cleanup;
477
+ }
478
+ buffer_start++;
479
+ }
480
+
481
+ while (buffer_start < buffer_end) {
482
+ struct packref *ref = NULL;
483
+
484
+ error = packed_parse_oid(&ref, &buffer_start, buffer_end);
485
+ if (error < GIT_SUCCESS)
486
+ goto cleanup;
487
+
488
+ if (buffer_start[0] == '^') {
489
+ error = packed_parse_peel(ref, &buffer_start, buffer_end);
490
+ if (error < GIT_SUCCESS)
491
+ goto cleanup;
492
+ }
493
+
494
+ error = git_hashtable_insert(ref_cache->packfile, ref->name, ref);
495
+ if (error < GIT_SUCCESS) {
496
+ free(ref);
497
+ goto cleanup;
498
+ }
499
+ }
500
+
501
+ git_futils_freebuffer(&packfile);
502
+ return GIT_SUCCESS;
503
+
504
+ cleanup:
505
+ git_hashtable_free(ref_cache->packfile);
506
+ ref_cache->packfile = NULL;
507
+ git_futils_freebuffer(&packfile);
508
+ return git__rethrow(error, "Failed to load packed references");
509
+ }
510
+
511
+
512
+ struct dirent_list_data {
513
+ git_repository *repo;
514
+ size_t repo_path_len;
515
+ unsigned int list_flags;
516
+
517
+ int (*callback)(const char *, void *);
518
+ void *callback_payload;
519
+ };
520
+
521
+ static int _dirent_loose_listall(void *_data, char *full_path)
522
+ {
523
+ struct dirent_list_data *data = (struct dirent_list_data *)_data;
524
+ char *file_path = full_path + data->repo_path_len;
525
+
526
+ if (git_futils_isdir(full_path) == GIT_SUCCESS)
527
+ return git_futils_direach(full_path, GIT_PATH_MAX,
528
+ _dirent_loose_listall, _data);
529
+
530
+ /* do not add twice a reference that exists already in the packfile */
531
+ if ((data->list_flags & GIT_REF_PACKED) != 0 &&
532
+ git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
533
+ return GIT_SUCCESS;
534
+
535
+ if (data->list_flags != GIT_REF_LISTALL) {
536
+ if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
537
+ return GIT_SUCCESS; /* we are filtering out this reference */
538
+ }
539
+
540
+ return data->callback(file_path, data->callback_payload);
541
+ }
542
+
543
+ static int _dirent_loose_load(void *data, char *full_path)
544
+ {
545
+ git_repository *repository = (git_repository *)data;
546
+ void *old_ref = NULL;
547
+ struct packref *ref;
548
+ char *file_path;
549
+ int error;
550
+
551
+ if (git_futils_isdir(full_path) == GIT_SUCCESS)
552
+ return git_futils_direach(
553
+ full_path, GIT_PATH_MAX,
554
+ _dirent_loose_load, repository);
555
+
556
+ file_path = full_path + strlen(repository->path_repository);
557
+ error = loose_lookup_to_packfile(&ref, repository, file_path);
558
+
559
+ if (error == GIT_SUCCESS) {
560
+
561
+ if (git_hashtable_insert2(
562
+ repository->references.packfile,
563
+ ref->name, ref, &old_ref) < GIT_SUCCESS) {
564
+ free(ref);
565
+ return GIT_ENOMEM;
566
+ }
567
+
568
+ if (old_ref != NULL)
569
+ free(old_ref);
570
+ }
571
+
572
+ return error == GIT_SUCCESS ?
573
+ GIT_SUCCESS :
574
+ git__rethrow(error, "Failed to load loose references into packfile");
575
+ }
576
+
577
+ /*
578
+ * Load all the loose references from the repository
579
+ * into the in-memory Packfile, and build a vector with
580
+ * all the references so it can be written back to
581
+ * disk.
582
+ */
583
+ static int packed_loadloose(git_repository *repository)
584
+ {
585
+ char refs_path[GIT_PATH_MAX];
586
+
587
+ /* the packfile must have been previously loaded! */
588
+ assert(repository->references.packfile);
589
+
590
+ git_path_join(refs_path, repository->path_repository, GIT_REFS_DIR);
591
+
592
+ /*
593
+ * Load all the loose files from disk into the Packfile table.
594
+ * This will overwrite any old packed entries with their
595
+ * updated loose versions
596
+ */
597
+ return git_futils_direach(
598
+ refs_path, GIT_PATH_MAX,
599
+ _dirent_loose_load, repository);
600
+ }
601
+
602
+ /*
603
+ * Write a single reference into a packfile
604
+ */
605
+ static int packed_write_ref(struct packref *ref, git_filebuf *file)
606
+ {
607
+ int error;
608
+ char oid[GIT_OID_HEXSZ + 1];
609
+
610
+ git_oid_fmt(oid, &ref->oid);
611
+ oid[GIT_OID_HEXSZ] = 0;
612
+
613
+ /*
614
+ * For references that peel to an object in the repo, we must
615
+ * write the resulting peel on a separate line, e.g.
616
+ *
617
+ * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
618
+ * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
619
+ *
620
+ * This obviously only applies to tags.
621
+ * The required peels have already been loaded into `ref->peel_target`.
622
+ */
623
+ if (ref->flags & GIT_PACKREF_HAS_PEEL) {
624
+ char peel[GIT_OID_HEXSZ + 1];
625
+ git_oid_fmt(peel, &ref->peel);
626
+ peel[GIT_OID_HEXSZ] = 0;
627
+
628
+ error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel);
629
+ } else {
630
+ error = git_filebuf_printf(file, "%s %s\n", oid, ref->name);
631
+ }
632
+
633
+ return error == GIT_SUCCESS ?
634
+ GIT_SUCCESS :
635
+ git__rethrow(error, "Failed to write packed reference");
636
+ }
637
+
638
+ /*
639
+ * Find out what object this reference resolves to.
640
+ *
641
+ * For references that point to a 'big' tag (e.g. an
642
+ * actual tag object on the repository), we need to
643
+ * cache on the packfile the OID of the object to
644
+ * which that 'big tag' is pointing to.
645
+ */
646
+ static int packed_find_peel(git_repository *repo, struct packref *ref)
647
+ {
648
+ git_object *object;
649
+ int error;
650
+
651
+ if (ref->flags & GIT_PACKREF_HAS_PEEL)
652
+ return GIT_SUCCESS;
653
+
654
+ /*
655
+ * Only applies to tags, i.e. references
656
+ * in the /refs/tags folder
657
+ */
658
+ if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
659
+ return GIT_SUCCESS;
660
+
661
+ /*
662
+ * Find the tagged object in the repository
663
+ */
664
+ error = git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY);
665
+ if (error < GIT_SUCCESS)
666
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference");
667
+
668
+ /*
669
+ * If the tagged object is a Tag object, we need to resolve it;
670
+ * if the ref is actually a 'weak' ref, we don't need to resolve
671
+ * anything.
672
+ */
673
+ if (git_object_type(object) == GIT_OBJ_TAG) {
674
+ git_tag *tag = (git_tag *)object;
675
+
676
+ /*
677
+ * Find the object pointed at by this tag
678
+ */
679
+ git_oid_cpy(&ref->peel, git_tag_target_oid(tag));
680
+ ref->flags |= GIT_PACKREF_HAS_PEEL;
681
+
682
+ /*
683
+ * The reference has now cached the resolved OID, and is
684
+ * marked at such. When written to the packfile, it'll be
685
+ * accompanied by this resolved oid
686
+ */
687
+ }
688
+
689
+ git_object_free(object);
690
+ return GIT_SUCCESS;
691
+ }
692
+
693
+ /*
694
+ * Remove all loose references
695
+ *
696
+ * Once we have successfully written a packfile,
697
+ * all the loose references that were packed must be
698
+ * removed from disk.
699
+ *
700
+ * This is a dangerous method; make sure the packfile
701
+ * is well-written, because we are destructing references
702
+ * here otherwise.
703
+ */
704
+ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
705
+ {
706
+ unsigned int i;
707
+ char full_path[GIT_PATH_MAX];
708
+ int error = GIT_SUCCESS;
709
+
710
+ for (i = 0; i < packing_list->length; ++i) {
711
+ struct packref *ref = git_vector_get(packing_list, i);
712
+
713
+ if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
714
+ continue;
715
+
716
+ git_path_join(full_path, repo->path_repository, ref->name);
717
+
718
+ if (git_futils_exists(full_path) == GIT_SUCCESS &&
719
+ p_unlink(full_path) < GIT_SUCCESS)
720
+ error = GIT_EOSERR;
721
+
722
+ /*
723
+ * if we fail to remove a single file, this is *not* good,
724
+ * but we should keep going and remove as many as possible.
725
+ * After we've removed as many files as possible, we return
726
+ * the error code anyway.
727
+ */
728
+ }
729
+
730
+ return error == GIT_SUCCESS ?
731
+ GIT_SUCCESS :
732
+ git__rethrow(error, "Failed to remove loose packed reference");
733
+ }
734
+
735
+ static int packed_sort(const void *a, const void *b)
736
+ {
737
+ const struct packref *ref_a = (const struct packref *)a;
738
+ const struct packref *ref_b = (const struct packref *)b;
739
+
740
+ return strcmp(ref_a->name, ref_b->name);
741
+ }
742
+
743
+ /*
744
+ * Write all the contents in the in-memory packfile to disk.
745
+ */
746
+ static int packed_write(git_repository *repo)
747
+ {
748
+ git_filebuf pack_file = GIT_FILEBUF_INIT;
749
+ int error;
750
+ unsigned int i;
751
+ char pack_file_path[GIT_PATH_MAX];
752
+
753
+ git_vector packing_list;
754
+ size_t total_refs;
755
+
756
+ assert(repo && repo->references.packfile);
757
+
758
+ total_refs = repo->references.packfile->key_count;
759
+ if ((error =
760
+ git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
761
+ return git__rethrow(error, "Failed to init packed refernces list");
762
+
763
+ /* Load all the packfile into a vector */
764
+ {
765
+ struct packref *reference;
766
+ const void *GIT_UNUSED(_unused);
767
+
768
+ GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
769
+ /* cannot fail: vector already has the right size */
770
+ git_vector_insert(&packing_list, reference);
771
+ );
772
+ }
773
+
774
+ /* sort the vector so the entries appear sorted on the packfile */
775
+ git_vector_sort(&packing_list);
776
+
777
+ /* Now we can open the file! */
778
+ git_path_join(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
779
+ if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS)
780
+ return git__rethrow(error, "Failed to write open packed references file");
781
+
782
+ /* Packfiles have a header... apparently
783
+ * This is in fact not required, but we might as well print it
784
+ * just for kicks */
785
+ if ((error =
786
+ git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS)
787
+ return git__rethrow(error, "Failed to write packed references file header");
788
+
789
+ for (i = 0; i < packing_list.length; ++i) {
790
+ struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
791
+
792
+ if ((error = packed_find_peel(repo, ref)) < GIT_SUCCESS) {
793
+ error = git__throw(GIT_EOBJCORRUPTED,
794
+ "A reference cannot be peeled");
795
+ goto cleanup;
796
+ }
797
+
798
+ if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
799
+ goto cleanup;
800
+ }
801
+
802
+ cleanup:
803
+ /* if we've written all the references properly, we can commit
804
+ * the packfile to make the changes effective */
805
+ if (error == GIT_SUCCESS) {
806
+ error = git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE);
807
+
808
+ /* when and only when the packfile has been properly written,
809
+ * we can go ahead and remove the loose refs */
810
+ if (error == GIT_SUCCESS) {
811
+ struct stat st;
812
+
813
+ error = packed_remove_loose(repo, &packing_list);
814
+
815
+ if (p_stat(pack_file_path, &st) == GIT_SUCCESS)
816
+ repo->references.packfile_time = st.st_mtime;
817
+ }
818
+ }
819
+ else git_filebuf_cleanup(&pack_file);
820
+
821
+ git_vector_free(&packing_list);
822
+
823
+ return error == GIT_SUCCESS ?
824
+ GIT_SUCCESS :
825
+ git__rethrow(error, "Failed to write packed references file");
826
+ }
827
+
828
+ static int _reference_available_cb(const char *ref, void *data)
829
+ {
830
+ const char *new, *old;
831
+ const char **refs;
832
+
833
+ assert(ref && data);
834
+
835
+ refs = (const char **)data;
836
+
837
+ new = (const char *)refs[0];
838
+ old = (const char *)refs[1];
839
+
840
+ if (!old || strcmp(old, ref)) {
841
+ int reflen = strlen(ref);
842
+ int newlen = strlen(new);
843
+ int cmplen = reflen < newlen ? reflen : newlen;
844
+ const char *lead = reflen < newlen ? new : ref;
845
+
846
+ if (!strncmp(new, ref, cmplen) &&
847
+ lead[cmplen] == '/')
848
+ return GIT_EEXISTS;
849
+ }
850
+
851
+ return GIT_SUCCESS;
852
+ }
853
+
854
+ static int reference_available(
855
+ git_repository *repo,
856
+ const char *ref,
857
+ const char* old_ref)
858
+ {
859
+ const char *refs[2];
860
+
861
+ refs[0] = ref;
862
+ refs[1] = old_ref;
863
+
864
+ if (git_reference_foreach(repo, GIT_REF_LISTALL,
865
+ _reference_available_cb, (void *)refs) < 0) {
866
+ return git__throw(GIT_EEXISTS,
867
+ "Reference name `%s` conflicts with existing reference", ref);
868
+ }
869
+
870
+ return GIT_SUCCESS;
871
+ }
872
+
873
+ static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
874
+ {
875
+ int error;
876
+ char ref_path[GIT_PATH_MAX];
877
+
878
+ error = packed_load(repo);
879
+ if (error < GIT_SUCCESS)
880
+ return git__rethrow(error, "Cannot resolve if a reference exists");
881
+
882
+ git_path_join(ref_path, repo->path_repository, ref_name);
883
+
884
+ if (git_futils_isfile(ref_path) == GIT_SUCCESS ||
885
+ git_hashtable_lookup(repo->references.packfile, ref_path) != NULL) {
886
+ *exists = 1;
887
+ } else {
888
+ *exists = 0;
889
+ }
890
+
891
+ return GIT_SUCCESS;
892
+ }
893
+
894
+ static int packed_lookup(git_reference *ref)
895
+ {
896
+ int error;
897
+ struct packref *pack_ref = NULL;
898
+
899
+ error = packed_load(ref->owner);
900
+ if (error < GIT_SUCCESS)
901
+ return git__rethrow(error,
902
+ "Failed to lookup reference from packfile");
903
+
904
+ if (ref->flags & GIT_REF_PACKED &&
905
+ ref->mtime == ref->owner->references.packfile_time)
906
+ return GIT_SUCCESS;
907
+
908
+ if (ref->flags & GIT_REF_SYMBOLIC)
909
+ free(ref->target.symbolic);
910
+
911
+ /* Look up on the packfile */
912
+ pack_ref = git_hashtable_lookup(ref->owner->references.packfile, ref->name);
913
+ if (pack_ref == NULL)
914
+ return git__throw(GIT_ENOTFOUND,
915
+ "Failed to lookup reference from packfile");
916
+
917
+ ref->flags = GIT_REF_OID | GIT_REF_PACKED;
918
+ ref->mtime = ref->owner->references.packfile_time;
919
+ git_oid_cpy(&ref->target.oid, &pack_ref->oid);
920
+
921
+ return GIT_SUCCESS;
922
+ }
923
+
924
+ static int reference_lookup(git_reference *ref)
925
+ {
926
+ int error_loose, error_packed;
927
+
928
+ error_loose = loose_lookup(ref);
929
+ if (error_loose == GIT_SUCCESS)
930
+ return GIT_SUCCESS;
931
+
932
+ error_packed = packed_lookup(ref);
933
+ if (error_packed == GIT_SUCCESS)
934
+ return GIT_SUCCESS;
935
+
936
+ git_reference_free(ref);
937
+
938
+ if (error_loose != GIT_ENOTFOUND)
939
+ return git__rethrow(error_loose, "Failed to lookup reference");
940
+
941
+ if (error_packed != GIT_ENOTFOUND)
942
+ return git__rethrow(error_packed, "Failed to lookup reference");
943
+
944
+ return git__throw(GIT_ENOTFOUND, "Reference not found");
945
+ }
946
+
947
+ /*
948
+ * Delete a reference.
949
+ * This is an internal method; the reference is removed
950
+ * from disk or the packfile, but the pointer is not freed
951
+ */
952
+ static int reference_delete(git_reference *ref)
953
+ {
954
+ int error;
955
+
956
+ assert(ref);
957
+
958
+ /* If the reference is packed, this is an expensive operation.
959
+ * We need to reload the packfile, remove the reference from the
960
+ * packing list, and repack */
961
+ if (ref->flags & GIT_REF_PACKED) {
962
+ /* load the existing packfile */
963
+ if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
964
+ return git__rethrow(error, "Failed to delete reference");
965
+
966
+ if (git_hashtable_remove(ref->owner->references.packfile,
967
+ ref->name) < GIT_SUCCESS)
968
+ return git__throw(GIT_ENOTFOUND, "Reference not found");
969
+
970
+ error = packed_write(ref->owner);
971
+
972
+ /* If the reference is loose, we can just remove the reference
973
+ * from the filesystem */
974
+ } else {
975
+ char full_path[GIT_PATH_MAX];
976
+ git_reference *ref_in_pack;
977
+
978
+ git_path_join(full_path, ref->owner->path_repository, ref->name);
979
+
980
+ error = p_unlink(full_path);
981
+ if (error < GIT_SUCCESS)
982
+ goto cleanup;
983
+
984
+ /* When deleting a loose reference, we have to ensure that an older
985
+ * packed version of it doesn't exist */
986
+ if (git_reference_lookup(&ref_in_pack, ref->owner,
987
+ ref->name) == GIT_SUCCESS) {
988
+ assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);
989
+ error = git_reference_delete(ref_in_pack);
990
+ }
991
+ }
992
+
993
+ cleanup:
994
+ return error == GIT_SUCCESS ?
995
+ GIT_SUCCESS :
996
+ git__rethrow(error, "Failed to delete reference");
997
+ }
998
+
999
+ int git_reference_delete(git_reference *ref)
1000
+ {
1001
+ int error = reference_delete(ref);
1002
+ if (error < GIT_SUCCESS)
1003
+ return error;
1004
+
1005
+ git_reference_free(ref);
1006
+ return GIT_SUCCESS;
1007
+ }
1008
+
1009
+
1010
+ int git_reference_lookup(git_reference **ref_out,
1011
+ git_repository *repo, const char *name)
1012
+ {
1013
+ int error;
1014
+ char normalized_name[GIT_REFNAME_MAX];
1015
+ git_reference *ref = NULL;
1016
+
1017
+ assert(ref_out && repo && name);
1018
+
1019
+ *ref_out = NULL;
1020
+
1021
+ error = normalize_name(normalized_name, sizeof(normalized_name), name, 0);
1022
+ if (error < GIT_SUCCESS)
1023
+ return git__rethrow(error, "Failed to lookup reference");
1024
+
1025
+ error = reference_create(&ref, repo, normalized_name);
1026
+ if (error < GIT_SUCCESS)
1027
+ return git__rethrow(error, "Failed to lookup reference");
1028
+
1029
+ error = reference_lookup(ref);
1030
+ if (error < GIT_SUCCESS)
1031
+ return git__rethrow(error, "Failed to lookup reference");
1032
+
1033
+ *ref_out = ref;
1034
+ return GIT_SUCCESS;
1035
+ }
1036
+
1037
+ /**
1038
+ * Getters
1039
+ */
1040
+ git_rtype git_reference_type(git_reference *ref)
1041
+ {
1042
+ assert(ref);
1043
+
1044
+ if (ref->flags & GIT_REF_OID)
1045
+ return GIT_REF_OID;
1046
+
1047
+ if (ref->flags & GIT_REF_SYMBOLIC)
1048
+ return GIT_REF_SYMBOLIC;
1049
+
1050
+ return GIT_REF_INVALID;
1051
+ }
1052
+
1053
+ int git_reference_is_packed(git_reference *ref)
1054
+ {
1055
+ assert(ref);
1056
+ return !!(ref->flags & GIT_REF_PACKED);
1057
+ }
1058
+
1059
+ const char *git_reference_name(git_reference *ref)
1060
+ {
1061
+ assert(ref);
1062
+ return ref->name;
1063
+ }
1064
+
1065
+ git_repository *git_reference_owner(git_reference *ref)
1066
+ {
1067
+ assert(ref);
1068
+ return ref->owner;
1069
+ }
1070
+
1071
+ const git_oid *git_reference_oid(git_reference *ref)
1072
+ {
1073
+ assert(ref);
1074
+
1075
+ if ((ref->flags & GIT_REF_OID) == 0)
1076
+ return NULL;
1077
+
1078
+ return &ref->target.oid;
1079
+ }
1080
+
1081
+ const char *git_reference_target(git_reference *ref)
1082
+ {
1083
+ assert(ref);
1084
+
1085
+ if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
1086
+ return NULL;
1087
+
1088
+ return ref->target.symbolic;
1089
+ }
1090
+
1091
+ int git_reference_create_symbolic(
1092
+ git_reference **ref_out,
1093
+ git_repository *repo,
1094
+ const char *name,
1095
+ const char *target,
1096
+ int force)
1097
+ {
1098
+ char normalized[GIT_REFNAME_MAX];
1099
+ int ref_exists, error = GIT_SUCCESS;
1100
+ git_reference *ref = NULL;
1101
+
1102
+ error = normalize_name(normalized, sizeof(normalized), name, 0);
1103
+ if (error < GIT_SUCCESS)
1104
+ goto cleanup;
1105
+
1106
+ if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
1107
+ return git__rethrow(error, "Failed to create symbolic reference");
1108
+
1109
+ if (ref_exists && !force)
1110
+ return git__throw(GIT_EEXISTS,
1111
+ "Failed to create symbolic reference. Reference already exists");
1112
+
1113
+ error = reference_create(&ref, repo, normalized);
1114
+ if (error < GIT_SUCCESS)
1115
+ goto cleanup;
1116
+
1117
+ ref->flags |= GIT_REF_SYMBOLIC;
1118
+
1119
+ /* set the target; this will normalize the name automatically
1120
+ * and write the reference on disk */
1121
+ error = git_reference_set_target(ref, target);
1122
+ if (error < GIT_SUCCESS)
1123
+ goto cleanup;
1124
+
1125
+ if (ref_out == NULL) {
1126
+ git_reference_free(ref);
1127
+ } else {
1128
+ *ref_out = ref;
1129
+ }
1130
+
1131
+ return GIT_SUCCESS;
1132
+
1133
+ cleanup:
1134
+ git_reference_free(ref);
1135
+ return git__rethrow(error, "Failed to create symbolic reference");
1136
+ }
1137
+
1138
+ int git_reference_create_oid(
1139
+ git_reference **ref_out,
1140
+ git_repository *repo,
1141
+ const char *name,
1142
+ const git_oid *id,
1143
+ int force)
1144
+ {
1145
+ int error = GIT_SUCCESS, ref_exists;
1146
+ git_reference *ref = NULL;
1147
+ char normalized[GIT_REFNAME_MAX];
1148
+
1149
+ error = normalize_name(normalized, sizeof(normalized), name, 1);
1150
+ if (error < GIT_SUCCESS)
1151
+ goto cleanup;
1152
+
1153
+ if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
1154
+ return git__rethrow(error, "Failed to create OID reference");
1155
+
1156
+ if (ref_exists && !force)
1157
+ return git__throw(GIT_EEXISTS,
1158
+ "Failed to create OID reference. Reference already exists");
1159
+
1160
+ if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
1161
+ return git__rethrow(error, "Failed to create reference");
1162
+
1163
+ error = reference_create(&ref, repo, name);
1164
+ if (error < GIT_SUCCESS)
1165
+ goto cleanup;
1166
+
1167
+ ref->flags |= GIT_REF_OID;
1168
+
1169
+ /* set the oid; this will write the reference on disk */
1170
+ error = git_reference_set_oid(ref, id);
1171
+ if (error < GIT_SUCCESS)
1172
+ goto cleanup;
1173
+
1174
+ if (ref_out == NULL) {
1175
+ git_reference_free(ref);
1176
+ } else {
1177
+ *ref_out = ref;
1178
+ }
1179
+
1180
+ return GIT_SUCCESS;
1181
+
1182
+ cleanup:
1183
+ git_reference_free(ref);
1184
+ return git__rethrow(error, "Failed to create reference OID");
1185
+ }
1186
+
1187
+ /*
1188
+ * Change the OID target of a reference.
1189
+ *
1190
+ * For both loose and packed references, just change
1191
+ * the oid in memory and (over)write the file in disk.
1192
+ *
1193
+ * We do not repack packed references because of performance
1194
+ * reasons.
1195
+ */
1196
+ int git_reference_set_oid(git_reference *ref, const git_oid *id)
1197
+ {
1198
+ int error = GIT_SUCCESS, exists;
1199
+ git_odb *odb = NULL;
1200
+
1201
+ if ((ref->flags & GIT_REF_OID) == 0)
1202
+ return git__throw(GIT_EINVALIDREFSTATE,
1203
+ "Failed to set OID target of reference. Not an OID reference");
1204
+
1205
+ assert(ref->owner);
1206
+
1207
+ error = git_repository_odb__weakptr(&odb, ref->owner);
1208
+ if (error < GIT_SUCCESS)
1209
+ return error;
1210
+
1211
+ exists = git_odb_exists(odb, id);
1212
+
1213
+ git_odb_free(odb);
1214
+
1215
+ /* Don't let the user create references to OIDs that
1216
+ * don't exist in the ODB */
1217
+ if (!exists)
1218
+ return git__throw(GIT_ENOTFOUND,
1219
+ "Failed to set OID target of reference. OID doesn't exist in ODB");
1220
+
1221
+ /* Update the OID value on `ref` */
1222
+ git_oid_cpy(&ref->target.oid, id);
1223
+
1224
+ /* Write back to disk */
1225
+ error = loose_write(ref);
1226
+ if (error < GIT_SUCCESS)
1227
+ return git__rethrow(error, "Failed to set OID target of reference");
1228
+
1229
+ return GIT_SUCCESS;
1230
+ }
1231
+
1232
+ /*
1233
+ * Change the target of a symbolic reference.
1234
+ *
1235
+ * This is easy because symrefs cannot be inside
1236
+ * a pack. We just change the target in memory
1237
+ * and overwrite the file on disk.
1238
+ */
1239
+ int git_reference_set_target(git_reference *ref, const char *target)
1240
+ {
1241
+ int error;
1242
+ char normalized[GIT_REFNAME_MAX];
1243
+
1244
+ if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
1245
+ return git__throw(GIT_EINVALIDREFSTATE,
1246
+ "Failed to set reference target. Not a symbolic reference");
1247
+
1248
+ error = normalize_name(normalized, sizeof(normalized), target, 0);
1249
+ if (error < GIT_SUCCESS)
1250
+ return git__rethrow(error,
1251
+ "Failed to set reference target. Invalid target name");
1252
+
1253
+ git__free(ref->target.symbolic);
1254
+ ref->target.symbolic = git__strdup(normalized);
1255
+ if (ref->target.symbolic == NULL)
1256
+ return GIT_ENOMEM;
1257
+
1258
+ return loose_write(ref);
1259
+ }
1260
+
1261
+ int git_reference_rename(git_reference *ref, const char *new_name, int force)
1262
+ {
1263
+ int error;
1264
+
1265
+ char aux_path[GIT_PATH_MAX];
1266
+ char normalized[GIT_REFNAME_MAX];
1267
+
1268
+ const char *head_target = NULL;
1269
+ git_reference *existing_ref = NULL, *head = NULL;
1270
+
1271
+ error = normalize_name(normalized, sizeof(normalized),
1272
+ new_name, ref->flags & GIT_REF_OID);
1273
+
1274
+ if (error < GIT_SUCCESS)
1275
+ return git__rethrow(error, "Failed to rename reference. Invalid name");
1276
+
1277
+ new_name = normalized;
1278
+
1279
+ /* If we are forcing the rename, try to lookup a reference with the
1280
+ * new one. If the lookup succeeds, we need to delete that ref
1281
+ * before the renaming can proceed */
1282
+ if (force) {
1283
+ error = git_reference_lookup(&existing_ref, ref->owner, new_name);
1284
+
1285
+ if (error == GIT_SUCCESS) {
1286
+ error = git_reference_delete(existing_ref);
1287
+ if (error < GIT_SUCCESS)
1288
+ return git__rethrow(error,
1289
+ "Failed to rename reference. "
1290
+ "The existing reference cannot be deleted");
1291
+ } else if (error != GIT_ENOTFOUND)
1292
+ goto cleanup;
1293
+
1294
+ /* If we're not forcing the rename, check if the reference exists.
1295
+ * If it does, renaming cannot continue */
1296
+ } else {
1297
+ int exists;
1298
+
1299
+ error = reference_exists(&exists, ref->owner, normalized);
1300
+ if (error < GIT_SUCCESS)
1301
+ goto cleanup;
1302
+
1303
+ if (exists)
1304
+ return git__throw(GIT_EEXISTS,
1305
+ "Failed to rename reference. Reference already exists");
1306
+ }
1307
+
1308
+ if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
1309
+ return git__rethrow(error,
1310
+ "Failed to rename reference. Reference already exists");
1311
+
1312
+ /*
1313
+ * Now delete the old ref and remove an possibly existing directory
1314
+ * named `new_name`. Note that using the internal `reference_delete`
1315
+ * method deletes the ref from disk but doesn't free the pointer, so
1316
+ * we can still access the ref's attributes for creating the new one
1317
+ */
1318
+ if ((error = reference_delete(ref)) < GIT_SUCCESS)
1319
+ goto cleanup;
1320
+
1321
+ git_path_join(aux_path, ref->owner->path_repository, new_name);
1322
+ if (git_futils_exists(aux_path) == GIT_SUCCESS) {
1323
+ if (git_futils_isdir(aux_path) == GIT_SUCCESS) {
1324
+ if ((error = git_futils_rmdir_r(aux_path, 0)) < GIT_SUCCESS)
1325
+ goto rollback;
1326
+ } else goto rollback;
1327
+ }
1328
+
1329
+ /*
1330
+ * Finally we can create the new reference.
1331
+ */
1332
+ if (ref->flags & GIT_REF_SYMBOLIC) {
1333
+ error = git_reference_create_symbolic(
1334
+ NULL, ref->owner, new_name, ref->target.symbolic, 0);
1335
+ } else {
1336
+ error = git_reference_create_oid(
1337
+ NULL, ref->owner, new_name, &ref->target.oid, 0);
1338
+ }
1339
+
1340
+ if (error < GIT_SUCCESS)
1341
+ goto rollback;
1342
+
1343
+ /*
1344
+ * Check if we have to update HEAD.
1345
+ */
1346
+ error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE);
1347
+ if (error < GIT_SUCCESS)
1348
+ goto cleanup;
1349
+
1350
+ head_target = git_reference_target(head);
1351
+
1352
+ if (head_target && !strcmp(head_target, ref->name)) {
1353
+ error = git_reference_create_symbolic(
1354
+ &head, ref->owner, "HEAD", new_name, 1);
1355
+
1356
+ if (error < GIT_SUCCESS)
1357
+ goto cleanup;
1358
+ }
1359
+
1360
+ /*
1361
+ * Rename the reflog file.
1362
+ */
1363
+ git_path_join_n(aux_path, 3, ref->owner->path_repository,
1364
+ GIT_REFLOG_DIR, ref->name);
1365
+ if (git_futils_exists(aux_path) == GIT_SUCCESS)
1366
+ error = git_reflog_rename(ref, new_name);
1367
+
1368
+ /*
1369
+ * Change the name of the reference given by the user.
1370
+ */
1371
+ git__free(ref->name);
1372
+ ref->name = git__strdup(new_name);
1373
+
1374
+ /* The reference is no longer packed */
1375
+ ref->flags &= ~GIT_REF_PACKED;
1376
+
1377
+ cleanup:
1378
+ /* We no longer need the newly created reference nor the head */
1379
+ git_reference_free(head);
1380
+ return error == GIT_SUCCESS ?
1381
+ GIT_SUCCESS :
1382
+ git__rethrow(error, "Failed to rename reference");
1383
+
1384
+ rollback:
1385
+ /*
1386
+ * Try to create the old reference again.
1387
+ */
1388
+ if (ref->flags & GIT_REF_SYMBOLIC)
1389
+ error = git_reference_create_symbolic(
1390
+ NULL, ref->owner, ref->name, ref->target.symbolic, 0);
1391
+ else
1392
+ error = git_reference_create_oid(
1393
+ NULL, ref->owner, ref->name, &ref->target.oid, 0);
1394
+
1395
+ /* The reference is no longer packed */
1396
+ ref->flags &= ~GIT_REF_PACKED;
1397
+
1398
+ return error == GIT_SUCCESS ?
1399
+ git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") :
1400
+ git__rethrow(error, "Failed to rename reference. Failed to rollback");
1401
+ }
1402
+
1403
+ int git_reference_resolve(git_reference **ref_out, git_reference *ref)
1404
+ {
1405
+ int error, i = 0;
1406
+ git_repository *repo;
1407
+
1408
+ assert(ref);
1409
+
1410
+ *ref_out = NULL;
1411
+ repo = ref->owner;
1412
+
1413
+ /* If the reference is already resolved, we need to return a
1414
+ * copy. Instead of duplicating `ref`, we look it up again to
1415
+ * ensure the copy is out to date */
1416
+ if (ref->flags & GIT_REF_OID)
1417
+ return git_reference_lookup(ref_out, ref->owner, ref->name);
1418
+
1419
+ /* Otherwise, keep iterating until the reference is resolved */
1420
+ for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
1421
+ git_reference *new_ref;
1422
+
1423
+ error = git_reference_lookup(&new_ref, repo, ref->target.symbolic);
1424
+ if (error < GIT_SUCCESS)
1425
+ return git__rethrow(error, "Failed to resolve reference");
1426
+
1427
+ /* Free intermediate references, except for the original one
1428
+ * we've received */
1429
+ if (i > 0)
1430
+ git_reference_free(ref);
1431
+
1432
+ ref = new_ref;
1433
+
1434
+ /* When the reference we've just looked up is an OID, we've
1435
+ * successfully resolved the symbolic ref */
1436
+ if (ref->flags & GIT_REF_OID) {
1437
+ *ref_out = ref;
1438
+ return GIT_SUCCESS;
1439
+ }
1440
+ }
1441
+
1442
+ return git__throw(GIT_ENOMEM,
1443
+ "Failed to resolve reference. Reference is too nested");
1444
+ }
1445
+
1446
+ int git_reference_packall(git_repository *repo)
1447
+ {
1448
+ int error;
1449
+
1450
+ /* load the existing packfile */
1451
+ if ((error = packed_load(repo)) < GIT_SUCCESS)
1452
+ return git__rethrow(error, "Failed to pack references");
1453
+
1454
+ /* update it in-memory with all the loose references */
1455
+ if ((error = packed_loadloose(repo)) < GIT_SUCCESS)
1456
+ return git__rethrow(error, "Failed to pack references");
1457
+
1458
+ /* write it back to disk */
1459
+ return packed_write(repo);
1460
+ }
1461
+
1462
+ int git_reference_foreach(
1463
+ git_repository *repo,
1464
+ unsigned int list_flags,
1465
+ int (*callback)(const char *, void *),
1466
+ void *payload)
1467
+ {
1468
+ int error;
1469
+ struct dirent_list_data data;
1470
+ char refs_path[GIT_PATH_MAX];
1471
+
1472
+ /* list all the packed references first */
1473
+ if (list_flags & GIT_REF_PACKED) {
1474
+ const char *ref_name;
1475
+ void *GIT_UNUSED(_unused);
1476
+
1477
+ if ((error = packed_load(repo)) < GIT_SUCCESS)
1478
+ return git__rethrow(error, "Failed to list references");
1479
+
1480
+ GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
1481
+ if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
1482
+ return git__throw(error,
1483
+ "Failed to list references. User callback failed");
1484
+ );
1485
+ }
1486
+
1487
+ /* now list the loose references, trying not to
1488
+ * duplicate the ref names already in the packed-refs file */
1489
+
1490
+ data.repo_path_len = strlen(repo->path_repository);
1491
+ data.list_flags = list_flags;
1492
+ data.repo = repo;
1493
+ data.callback = callback;
1494
+ data.callback_payload = payload;
1495
+
1496
+ git_path_join(refs_path, repo->path_repository, GIT_REFS_DIR);
1497
+ return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
1498
+ }
1499
+
1500
+ static int cb__reflist_add(const char *ref, void *data)
1501
+ {
1502
+ return git_vector_insert((git_vector *)data, git__strdup(ref));
1503
+ }
1504
+
1505
+ int git_reference_listall(
1506
+ git_strarray *array,
1507
+ git_repository *repo,
1508
+ unsigned int list_flags)
1509
+ {
1510
+ int error;
1511
+ git_vector ref_list;
1512
+
1513
+ assert(array && repo);
1514
+
1515
+ array->strings = NULL;
1516
+ array->count = 0;
1517
+
1518
+ if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
1519
+ return GIT_ENOMEM;
1520
+
1521
+ error = git_reference_foreach(
1522
+ repo, list_flags, &cb__reflist_add, (void *)&ref_list);
1523
+
1524
+ if (error < GIT_SUCCESS) {
1525
+ git_vector_free(&ref_list);
1526
+ return error;
1527
+ }
1528
+
1529
+ array->strings = (char **)ref_list.contents;
1530
+ array->count = ref_list.length;
1531
+ return GIT_SUCCESS;
1532
+ }
1533
+
1534
+ int git_reference_reload(git_reference *ref)
1535
+ {
1536
+ int error = reference_lookup(ref);
1537
+
1538
+ if (error < GIT_SUCCESS) {
1539
+ git_reference_free(ref);
1540
+ return git__rethrow(error, "Failed to reload reference");
1541
+ }
1542
+
1543
+ return GIT_SUCCESS;
1544
+ }
1545
+
1546
+
1547
+ void git_repository__refcache_free(git_refcache *refs)
1548
+ {
1549
+ assert(refs);
1550
+
1551
+ if (refs->packfile) {
1552
+ const void *GIT_UNUSED(_unused);
1553
+ struct packref *reference;
1554
+
1555
+ GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference,
1556
+ free(reference);
1557
+ );
1558
+
1559
+ git_hashtable_free(refs->packfile);
1560
+ }
1561
+ }
1562
+
1563
+ static int is_valid_ref_char(char ch)
1564
+ {
1565
+ if ((unsigned) ch <= ' ')
1566
+ return 0;
1567
+
1568
+ switch (ch) {
1569
+ case '~':
1570
+ case '^':
1571
+ case ':':
1572
+ case '\\':
1573
+ case '?':
1574
+ case '[':
1575
+ case '*':
1576
+ return 0;
1577
+ default:
1578
+ return 1;
1579
+ }
1580
+ }
1581
+
1582
+ static int normalize_name(
1583
+ char *buffer_out,
1584
+ size_t out_size,
1585
+ const char *name,
1586
+ int is_oid_ref)
1587
+ {
1588
+ const char *name_end, *buffer_out_start;
1589
+ const char *current;
1590
+ int contains_a_slash = 0;
1591
+
1592
+ assert(name && buffer_out);
1593
+
1594
+ buffer_out_start = buffer_out;
1595
+ current = name;
1596
+ name_end = name + strlen(name);
1597
+
1598
+ /* Terminating null byte */
1599
+ out_size--;
1600
+
1601
+ /* A refname can not be empty */
1602
+ if (name_end == name)
1603
+ return git__throw(GIT_EINVALIDREFNAME,
1604
+ "Failed to normalize name. Reference name is empty");
1605
+
1606
+ /* A refname can not end with a dot or a slash */
1607
+ if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
1608
+ return git__throw(GIT_EINVALIDREFNAME,
1609
+ "Failed to normalize name. Reference name ends with dot or slash");
1610
+
1611
+ while (current < name_end && out_size) {
1612
+ if (!is_valid_ref_char(*current))
1613
+ return git__throw(GIT_EINVALIDREFNAME,
1614
+ "Failed to normalize name. "
1615
+ "Reference name contains invalid characters");
1616
+
1617
+ if (buffer_out > buffer_out_start) {
1618
+ char prev = *(buffer_out - 1);
1619
+
1620
+ /* A refname can not start with a dot nor contain a double dot */
1621
+ if (*current == '.' && ((prev == '.') || (prev == '/')))
1622
+ return git__throw(GIT_EINVALIDREFNAME,
1623
+ "Failed to normalize name. "
1624
+ "Reference name starts with a dot or contains a double dot");
1625
+
1626
+ /* '@{' is forbidden within a refname */
1627
+ if (*current == '{' && prev == '@')
1628
+ return git__throw(GIT_EINVALIDREFNAME,
1629
+ "Failed to normalize name. Reference name contains '@{'");
1630
+
1631
+ /* Prevent multiple slashes from being added to the output */
1632
+ if (*current == '/' && prev == '/') {
1633
+ current++;
1634
+ continue;
1635
+ }
1636
+ }
1637
+
1638
+ if (*current == '/')
1639
+ contains_a_slash = 1;
1640
+
1641
+ *buffer_out++ = *current++;
1642
+ out_size--;
1643
+ }
1644
+
1645
+ if (!out_size)
1646
+ return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long");
1647
+
1648
+ /* Object id refname have to contain at least one slash, except
1649
+ * for HEAD in a detached state or MERGE_HEAD if we're in the
1650
+ * middle of a merge */
1651
+ if (is_oid_ref &&
1652
+ !contains_a_slash &&
1653
+ strcmp(name, GIT_HEAD_FILE) != 0 &&
1654
+ strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
1655
+ strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
1656
+ return git__throw(GIT_EINVALIDREFNAME,
1657
+ "Failed to normalize name. Reference name contains no slashes");
1658
+
1659
+ /* A refname can not end with ".lock" */
1660
+ if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
1661
+ return git__throw(GIT_EINVALIDREFNAME,
1662
+ "Failed to normalize name. Reference name ends with '.lock'");
1663
+
1664
+ *buffer_out = '\0';
1665
+
1666
+ /*
1667
+ * For object id references, name has to start with refs/. Again,
1668
+ * we need to allow HEAD to be in a detached state.
1669
+ */
1670
+ if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
1671
+ strcmp(buffer_out_start, GIT_HEAD_FILE)))
1672
+ return git__throw(GIT_EINVALIDREFNAME,
1673
+ "Failed to normalize name. "
1674
+ "Reference name does not start with 'refs/'");
1675
+
1676
+ return GIT_SUCCESS;
1677
+ }
1678
+
1679
+ int git_reference__normalize_name(
1680
+ char *buffer_out,
1681
+ size_t out_size,
1682
+ const char *name)
1683
+ {
1684
+ return normalize_name(buffer_out, out_size, name, 0);
1685
+ }
1686
+
1687
+ int git_reference__normalize_name_oid(
1688
+ char *buffer_out,
1689
+ size_t out_size,
1690
+ const char *name)
1691
+ {
1692
+ return normalize_name(buffer_out, out_size, name, 1);
1693
+ }