rugged 0.19.0 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (668) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/README.md +184 -33
  4. data/ext/rugged/extconf.rb +111 -28
  5. data/ext/rugged/rugged.c +327 -89
  6. data/ext/rugged/rugged.h +64 -28
  7. data/ext/rugged/rugged_allocator.c +89 -0
  8. data/ext/rugged/rugged_backend.c +17 -0
  9. data/ext/rugged/rugged_blame.c +278 -0
  10. data/ext/rugged/rugged_blob.c +301 -75
  11. data/ext/rugged/rugged_branch.c +92 -242
  12. data/ext/rugged/rugged_branch_collection.c +388 -0
  13. data/ext/rugged/rugged_commit.c +575 -79
  14. data/ext/rugged/rugged_config.c +129 -36
  15. data/ext/rugged/rugged_cred.c +131 -0
  16. data/ext/rugged/rugged_diff.c +291 -122
  17. data/ext/rugged/rugged_diff_delta.c +16 -22
  18. data/ext/rugged/rugged_diff_hunk.c +35 -51
  19. data/ext/rugged/rugged_diff_line.c +23 -36
  20. data/ext/rugged/rugged_index.c +289 -152
  21. data/ext/rugged/rugged_note.c +50 -60
  22. data/ext/rugged/rugged_object.c +13 -30
  23. data/ext/rugged/rugged_patch.c +400 -0
  24. data/ext/rugged/rugged_rebase.c +397 -0
  25. data/ext/rugged/rugged_reference.c +76 -346
  26. data/ext/rugged/rugged_reference_collection.c +423 -0
  27. data/ext/rugged/rugged_remote.c +438 -461
  28. data/ext/rugged/rugged_remote_collection.c +435 -0
  29. data/ext/rugged/rugged_repo.c +1548 -365
  30. data/ext/rugged/rugged_revwalk.c +378 -99
  31. data/ext/rugged/rugged_settings.c +86 -23
  32. data/ext/rugged/rugged_signature.c +47 -37
  33. data/ext/rugged/rugged_submodule.c +835 -0
  34. data/ext/rugged/rugged_submodule_collection.c +366 -0
  35. data/ext/rugged/rugged_tag.c +88 -210
  36. data/ext/rugged/rugged_tag_collection.c +326 -0
  37. data/ext/rugged/rugged_tree.c +460 -217
  38. data/lib/rugged/attributes.rb +46 -0
  39. data/lib/rugged/blob.rb +33 -0
  40. data/lib/rugged/branch.rb +12 -16
  41. data/lib/rugged/commit.rb +9 -0
  42. data/lib/rugged/console.rb +5 -0
  43. data/lib/rugged/credentials.rb +48 -0
  44. data/lib/rugged/diff/delta.rb +6 -2
  45. data/lib/rugged/diff/hunk.rb +9 -9
  46. data/lib/rugged/diff/line.rb +28 -5
  47. data/lib/rugged/diff.rb +7 -1
  48. data/lib/rugged/index.rb +120 -0
  49. data/lib/rugged/object.rb +5 -0
  50. data/lib/rugged/patch.rb +41 -0
  51. data/lib/rugged/reference.rb +6 -3
  52. data/lib/rugged/remote.rb +5 -9
  53. data/lib/rugged/repository.rb +126 -14
  54. data/lib/rugged/submodule_collection.rb +53 -0
  55. data/lib/rugged/tag.rb +45 -16
  56. data/lib/rugged/tree.rb +163 -1
  57. data/lib/rugged/version.rb +6 -1
  58. data/lib/rugged/walker.rb +5 -0
  59. data/lib/rugged.rb +16 -1
  60. data/vendor/libgit2/AUTHORS +77 -0
  61. data/vendor/libgit2/CMakeLists.txt +317 -0
  62. data/vendor/libgit2/COPYING +993 -0
  63. data/vendor/libgit2/cmake/Modules/AddCFlagIfSupported.cmake +30 -0
  64. data/vendor/libgit2/cmake/Modules/CheckPrototypeDefinition.c.in +29 -0
  65. data/vendor/libgit2/cmake/Modules/CheckPrototypeDefinition.cmake +96 -0
  66. data/vendor/libgit2/cmake/Modules/EnableWarnings.cmake +11 -0
  67. data/vendor/libgit2/cmake/Modules/FindCoreFoundation.cmake +26 -0
  68. data/vendor/libgit2/cmake/Modules/FindGSSAPI.cmake +324 -0
  69. data/vendor/libgit2/cmake/Modules/FindHTTP_Parser.cmake +39 -0
  70. data/vendor/libgit2/cmake/Modules/FindIconv.cmake +45 -0
  71. data/vendor/libgit2/cmake/Modules/FindPkgLibraries.cmake +28 -0
  72. data/vendor/libgit2/cmake/Modules/FindSecurity.cmake +28 -0
  73. data/vendor/libgit2/cmake/Modules/FindStatNsec.cmake +20 -0
  74. data/vendor/libgit2/cmake/Modules/FindmbedTLS.cmake +93 -0
  75. data/vendor/libgit2/cmake/Modules/IdeSplitSources.cmake +22 -0
  76. data/vendor/libgit2/deps/http-parser/CMakeLists.txt +5 -0
  77. data/vendor/libgit2/deps/http-parser/COPYING +23 -0
  78. data/vendor/libgit2/deps/http-parser/http_parser.c +5 -2
  79. data/vendor/libgit2/deps/http-parser/http_parser.h +2 -0
  80. data/vendor/libgit2/deps/regex/CMakeLists.txt +2 -0
  81. data/vendor/libgit2/deps/regex/COPYING +502 -0
  82. data/vendor/libgit2/deps/regex/regex.c +10 -3
  83. data/vendor/libgit2/deps/winhttp/CMakeLists.txt +26 -0
  84. data/vendor/libgit2/deps/winhttp/COPYING.GPL +993 -0
  85. data/vendor/libgit2/deps/winhttp/COPYING.LGPL +502 -0
  86. data/vendor/libgit2/deps/winhttp/urlmon.h +45 -0
  87. data/vendor/libgit2/deps/winhttp/winhttp.def +29 -0
  88. data/vendor/libgit2/deps/winhttp/winhttp.h +594 -0
  89. data/vendor/libgit2/deps/winhttp/winhttp64.def +29 -0
  90. data/vendor/libgit2/deps/zlib/CMakeLists.txt +5 -0
  91. data/vendor/libgit2/deps/zlib/COPYING +27 -0
  92. data/vendor/libgit2/deps/zlib/adler32.c +51 -34
  93. data/vendor/libgit2/deps/zlib/crc32.c +61 -61
  94. data/vendor/libgit2/deps/zlib/crc32.h +1 -1
  95. data/vendor/libgit2/deps/zlib/deflate.c +681 -352
  96. data/vendor/libgit2/deps/zlib/deflate.h +25 -18
  97. data/vendor/libgit2/deps/zlib/gzguts.h +218 -0
  98. data/vendor/libgit2/deps/zlib/infback.c +640 -0
  99. data/vendor/libgit2/deps/zlib/inffast.c +36 -53
  100. data/vendor/libgit2/deps/zlib/inffixed.h +3 -3
  101. data/vendor/libgit2/deps/zlib/inflate.c +167 -86
  102. data/vendor/libgit2/deps/zlib/inflate.h +7 -4
  103. data/vendor/libgit2/deps/zlib/inftrees.c +24 -50
  104. data/vendor/libgit2/deps/zlib/trees.c +55 -96
  105. data/vendor/libgit2/deps/zlib/zconf.h +499 -19
  106. data/vendor/libgit2/deps/zlib/zlib.h +526 -227
  107. data/vendor/libgit2/deps/zlib/zutil.c +39 -32
  108. data/vendor/libgit2/deps/zlib/zutil.h +75 -78
  109. data/vendor/libgit2/include/git2/annotated_commit.h +125 -0
  110. data/vendor/libgit2/include/git2/apply.h +129 -0
  111. data/vendor/libgit2/include/git2/attr.h +36 -21
  112. data/vendor/libgit2/include/git2/blame.h +229 -0
  113. data/vendor/libgit2/include/git2/blob.h +81 -44
  114. data/vendor/libgit2/include/git2/branch.h +81 -42
  115. data/vendor/libgit2/include/git2/buffer.h +128 -0
  116. data/vendor/libgit2/include/git2/checkout.h +141 -67
  117. data/vendor/libgit2/include/git2/cherrypick.h +92 -0
  118. data/vendor/libgit2/include/git2/clone.h +157 -58
  119. data/vendor/libgit2/include/git2/commit.h +231 -12
  120. data/vendor/libgit2/include/git2/common.h +216 -30
  121. data/vendor/libgit2/include/git2/config.h +274 -48
  122. data/vendor/libgit2/include/git2/cred_helpers.h +4 -4
  123. data/vendor/libgit2/include/git2/deprecated.h +253 -0
  124. data/vendor/libgit2/include/git2/describe.h +189 -0
  125. data/vendor/libgit2/include/git2/diff.h +985 -575
  126. data/vendor/libgit2/include/git2/errors.h +93 -52
  127. data/vendor/libgit2/include/git2/filter.h +217 -0
  128. data/vendor/libgit2/include/git2/global.h +44 -0
  129. data/vendor/libgit2/include/git2/graph.h +17 -0
  130. data/vendor/libgit2/include/git2/ignore.h +2 -2
  131. data/vendor/libgit2/include/git2/index.h +269 -94
  132. data/vendor/libgit2/include/git2/indexer.h +44 -12
  133. data/vendor/libgit2/include/git2/mailmap.h +115 -0
  134. data/vendor/libgit2/include/git2/merge.h +501 -64
  135. data/vendor/libgit2/include/git2/message.h +52 -17
  136. data/vendor/libgit2/include/git2/net.h +11 -5
  137. data/vendor/libgit2/include/git2/notes.h +120 -16
  138. data/vendor/libgit2/include/git2/object.h +62 -23
  139. data/vendor/libgit2/include/git2/odb.h +140 -24
  140. data/vendor/libgit2/include/git2/odb_backend.h +56 -12
  141. data/vendor/libgit2/include/git2/oid.h +17 -18
  142. data/vendor/libgit2/include/git2/oidarray.h +40 -0
  143. data/vendor/libgit2/include/git2/pack.h +86 -7
  144. data/vendor/libgit2/include/git2/patch.h +274 -0
  145. data/vendor/libgit2/include/git2/pathspec.h +280 -0
  146. data/vendor/libgit2/include/git2/proxy.h +96 -0
  147. data/vendor/libgit2/include/git2/rebase.h +323 -0
  148. data/vendor/libgit2/include/git2/reflog.h +12 -9
  149. data/vendor/libgit2/include/git2/refs.h +241 -46
  150. data/vendor/libgit2/include/git2/refspec.h +20 -4
  151. data/vendor/libgit2/include/git2/remote.h +636 -209
  152. data/vendor/libgit2/include/git2/repository.h +267 -57
  153. data/vendor/libgit2/include/git2/reset.h +36 -6
  154. data/vendor/libgit2/include/git2/revert.h +91 -0
  155. data/vendor/libgit2/include/git2/revparse.h +27 -16
  156. data/vendor/libgit2/include/git2/revwalk.h +78 -35
  157. data/vendor/libgit2/include/git2/signature.h +32 -5
  158. data/vendor/libgit2/include/git2/stash.h +160 -21
  159. data/vendor/libgit2/include/git2/status.h +92 -30
  160. data/vendor/libgit2/include/git2/submodule.h +226 -133
  161. data/vendor/libgit2/include/git2/sys/alloc.h +101 -0
  162. data/vendor/libgit2/include/git2/sys/commit.h +38 -4
  163. data/vendor/libgit2/include/git2/sys/config.h +68 -9
  164. data/vendor/libgit2/include/git2/sys/diff.h +94 -0
  165. data/vendor/libgit2/include/git2/sys/filter.h +332 -0
  166. data/vendor/libgit2/include/git2/sys/hashsig.h +106 -0
  167. data/vendor/libgit2/include/git2/sys/index.h +6 -5
  168. data/vendor/libgit2/include/git2/sys/mempack.h +86 -0
  169. data/vendor/libgit2/include/git2/sys/merge.h +182 -0
  170. data/vendor/libgit2/include/git2/sys/odb_backend.h +66 -28
  171. data/vendor/libgit2/include/git2/sys/openssl.h +38 -0
  172. data/vendor/libgit2/include/git2/sys/path.h +64 -0
  173. data/vendor/libgit2/include/git2/sys/refdb_backend.h +79 -19
  174. data/vendor/libgit2/include/git2/sys/reflog.h +21 -0
  175. data/vendor/libgit2/include/git2/sys/refs.h +13 -2
  176. data/vendor/libgit2/include/git2/sys/repository.h +64 -1
  177. data/vendor/libgit2/include/git2/sys/stream.h +138 -0
  178. data/vendor/libgit2/include/git2/sys/time.h +31 -0
  179. data/vendor/libgit2/include/git2/sys/transport.h +439 -0
  180. data/vendor/libgit2/include/git2/tag.h +11 -2
  181. data/vendor/libgit2/include/git2/trace.h +1 -1
  182. data/vendor/libgit2/include/git2/transaction.h +121 -0
  183. data/vendor/libgit2/include/git2/transport.h +261 -292
  184. data/vendor/libgit2/include/git2/tree.h +111 -21
  185. data/vendor/libgit2/include/git2/types.h +244 -32
  186. data/vendor/libgit2/include/git2/version.h +5 -2
  187. data/vendor/libgit2/include/git2/worktree.h +255 -0
  188. data/vendor/libgit2/include/git2.h +50 -40
  189. data/vendor/libgit2/libgit2.pc.in +13 -0
  190. data/vendor/libgit2/src/CMakeLists.txt +525 -0
  191. data/vendor/libgit2/src/alloc.c +55 -0
  192. data/vendor/libgit2/src/alloc.h +40 -0
  193. data/vendor/libgit2/src/annotated_commit.c +228 -0
  194. data/vendor/libgit2/src/annotated_commit.h +52 -0
  195. data/vendor/libgit2/src/apply.c +855 -0
  196. data/vendor/libgit2/src/apply.h +25 -0
  197. data/vendor/libgit2/src/array.h +74 -16
  198. data/vendor/libgit2/src/attr.c +239 -408
  199. data/vendor/libgit2/src/attr.h +3 -33
  200. data/vendor/libgit2/src/attr_file.c +424 -156
  201. data/vendor/libgit2/src/attr_file.h +95 -23
  202. data/vendor/libgit2/src/attrcache.c +469 -0
  203. data/vendor/libgit2/src/attrcache.h +37 -5
  204. data/vendor/libgit2/src/bitvec.h +75 -0
  205. data/vendor/libgit2/src/blame.c +532 -0
  206. data/vendor/libgit2/src/blame.h +95 -0
  207. data/vendor/libgit2/src/blame_git.c +668 -0
  208. data/vendor/libgit2/src/blame_git.h +22 -0
  209. data/vendor/libgit2/src/blob.c +233 -129
  210. data/vendor/libgit2/src/blob.h +29 -1
  211. data/vendor/libgit2/src/branch.c +295 -197
  212. data/vendor/libgit2/src/branch.h +2 -0
  213. data/vendor/libgit2/src/buf_text.c +52 -27
  214. data/vendor/libgit2/src/buf_text.h +7 -7
  215. data/vendor/libgit2/src/buffer.c +609 -52
  216. data/vendor/libgit2/src/buffer.h +68 -23
  217. data/vendor/libgit2/src/cache.c +48 -51
  218. data/vendor/libgit2/src/cache.h +6 -4
  219. data/vendor/libgit2/src/cc-compat.h +35 -7
  220. data/vendor/libgit2/src/checkout.c +1827 -483
  221. data/vendor/libgit2/src/checkout.h +4 -1
  222. data/vendor/libgit2/src/cherrypick.c +230 -0
  223. data/vendor/libgit2/src/clone.c +338 -258
  224. data/vendor/libgit2/src/{compress.h → clone.h} +5 -5
  225. data/vendor/libgit2/src/commit.c +711 -124
  226. data/vendor/libgit2/src/commit.h +10 -3
  227. data/vendor/libgit2/src/commit_list.c +21 -14
  228. data/vendor/libgit2/src/commit_list.h +9 -3
  229. data/vendor/libgit2/src/common.h +153 -13
  230. data/vendor/libgit2/src/config.c +871 -242
  231. data/vendor/libgit2/src/config.h +58 -14
  232. data/vendor/libgit2/src/config_backend.h +84 -0
  233. data/vendor/libgit2/src/config_cache.c +44 -18
  234. data/vendor/libgit2/src/config_entries.c +259 -0
  235. data/vendor/libgit2/src/config_entries.h +23 -0
  236. data/vendor/libgit2/src/config_file.c +837 -1113
  237. data/vendor/libgit2/src/config_mem.c +224 -0
  238. data/vendor/libgit2/src/config_parse.c +558 -0
  239. data/vendor/libgit2/src/config_parse.h +64 -0
  240. data/vendor/libgit2/src/crlf.c +290 -195
  241. data/vendor/libgit2/src/date.c +35 -7
  242. data/vendor/libgit2/src/delta.c +275 -71
  243. data/vendor/libgit2/src/delta.h +80 -58
  244. data/vendor/libgit2/src/describe.c +893 -0
  245. data/vendor/libgit2/src/diff.c +330 -1128
  246. data/vendor/libgit2/src/diff.h +25 -67
  247. data/vendor/libgit2/src/diff_driver.c +225 -109
  248. data/vendor/libgit2/src/diff_driver.h +5 -2
  249. data/vendor/libgit2/src/diff_file.c +128 -103
  250. data/vendor/libgit2/src/diff_file.h +17 -12
  251. data/vendor/libgit2/src/diff_generate.c +1622 -0
  252. data/vendor/libgit2/src/diff_generate.h +128 -0
  253. data/vendor/libgit2/src/diff_parse.c +108 -0
  254. data/vendor/libgit2/src/diff_parse.h +20 -0
  255. data/vendor/libgit2/src/diff_print.c +578 -218
  256. data/vendor/libgit2/src/diff_stats.c +362 -0
  257. data/vendor/libgit2/src/diff_tform.c +429 -257
  258. data/vendor/libgit2/src/diff_tform.h +25 -0
  259. data/vendor/libgit2/src/diff_xdiff.c +143 -46
  260. data/vendor/libgit2/src/diff_xdiff.h +12 -5
  261. data/vendor/libgit2/src/errors.c +150 -34
  262. data/vendor/libgit2/src/features.h.in +37 -0
  263. data/vendor/libgit2/src/fetch.c +69 -46
  264. data/vendor/libgit2/src/fetch.h +6 -12
  265. data/vendor/libgit2/src/fetchhead.c +40 -33
  266. data/vendor/libgit2/src/fetchhead.h +5 -4
  267. data/vendor/libgit2/src/filebuf.c +163 -61
  268. data/vendor/libgit2/src/filebuf.h +13 -7
  269. data/vendor/libgit2/src/fileops.c +549 -407
  270. data/vendor/libgit2/src/fileops.h +97 -106
  271. data/vendor/libgit2/src/filter.c +989 -46
  272. data/vendor/libgit2/src/filter.h +21 -70
  273. data/vendor/libgit2/src/fnmatch.c +67 -11
  274. data/vendor/libgit2/src/fnmatch.h +27 -7
  275. data/vendor/libgit2/src/global.c +257 -63
  276. data/vendor/libgit2/src/global.h +19 -0
  277. data/vendor/libgit2/src/graph.c +39 -23
  278. data/vendor/libgit2/src/hash/hash_collisiondetect.h +51 -0
  279. data/vendor/libgit2/src/hash/hash_common_crypto.h +61 -0
  280. data/vendor/libgit2/src/hash/hash_generic.c +3 -3
  281. data/vendor/libgit2/src/hash/hash_generic.h +10 -5
  282. data/vendor/libgit2/src/hash/hash_mbedtls.c +38 -0
  283. data/vendor/libgit2/src/hash/hash_mbedtls.h +24 -0
  284. data/vendor/libgit2/src/hash/hash_openssl.h +26 -8
  285. data/vendor/libgit2/src/hash/hash_win32.c +71 -43
  286. data/vendor/libgit2/src/hash/hash_win32.h +4 -3
  287. data/vendor/libgit2/src/hash/sha1dc/sha1.c +1900 -0
  288. data/vendor/libgit2/src/hash/sha1dc/sha1.h +110 -0
  289. data/vendor/libgit2/src/hash/sha1dc/ubc_check.c +372 -0
  290. data/vendor/libgit2/src/hash/sha1dc/ubc_check.h +52 -0
  291. data/vendor/libgit2/src/hash.c +0 -1
  292. data/vendor/libgit2/src/hash.h +13 -6
  293. data/vendor/libgit2/src/hashsig.c +121 -126
  294. data/vendor/libgit2/src/ident.c +129 -0
  295. data/vendor/libgit2/src/idxmap.c +153 -0
  296. data/vendor/libgit2/src/idxmap.h +41 -0
  297. data/vendor/libgit2/src/ignore.c +362 -123
  298. data/vendor/libgit2/src/ignore.h +16 -4
  299. data/vendor/libgit2/src/index.c +2131 -692
  300. data/vendor/libgit2/src/index.h +138 -6
  301. data/vendor/libgit2/src/indexer.c +866 -266
  302. data/vendor/libgit2/src/indexer.h +16 -0
  303. data/vendor/libgit2/src/integer.h +106 -0
  304. data/vendor/libgit2/src/iterator.c +1888 -967
  305. data/vendor/libgit2/src/iterator.h +130 -67
  306. data/vendor/libgit2/src/khash.h +43 -29
  307. data/vendor/libgit2/src/mailmap.c +485 -0
  308. data/vendor/libgit2/src/mailmap.h +35 -0
  309. data/vendor/libgit2/src/map.h +1 -1
  310. data/vendor/libgit2/src/merge.c +1679 -479
  311. data/vendor/libgit2/src/merge.h +89 -22
  312. data/vendor/libgit2/src/merge_driver.c +426 -0
  313. data/vendor/libgit2/src/merge_driver.h +62 -0
  314. data/vendor/libgit2/src/merge_file.c +238 -101
  315. data/vendor/libgit2/src/message.c +4 -28
  316. data/vendor/libgit2/src/message.h +3 -1
  317. data/vendor/libgit2/src/mwindow.c +123 -15
  318. data/vendor/libgit2/src/mwindow.h +10 -1
  319. data/vendor/libgit2/src/netops.c +178 -499
  320. data/vendor/libgit2/src/netops.h +51 -27
  321. data/vendor/libgit2/src/notes.c +251 -94
  322. data/vendor/libgit2/src/notes.h +5 -2
  323. data/vendor/libgit2/src/object.c +253 -67
  324. data/vendor/libgit2/src/object.h +40 -2
  325. data/vendor/libgit2/src/object_api.c +30 -11
  326. data/vendor/libgit2/src/odb.c +765 -201
  327. data/vendor/libgit2/src/odb.h +40 -8
  328. data/vendor/libgit2/src/odb_loose.c +560 -346
  329. data/vendor/libgit2/src/odb_mempack.c +185 -0
  330. data/vendor/libgit2/src/odb_pack.c +117 -73
  331. data/vendor/libgit2/src/offmap.c +113 -0
  332. data/vendor/libgit2/src/offmap.h +32 -42
  333. data/vendor/libgit2/src/oid.c +45 -25
  334. data/vendor/libgit2/src/oid.h +26 -8
  335. data/vendor/libgit2/src/oidarray.c +34 -0
  336. data/vendor/libgit2/src/oidarray.h +20 -0
  337. data/vendor/libgit2/src/oidmap.c +125 -0
  338. data/vendor/libgit2/src/oidmap.h +30 -17
  339. data/vendor/libgit2/src/pack-objects.c +688 -265
  340. data/vendor/libgit2/src/pack-objects.h +27 -13
  341. data/vendor/libgit2/src/pack.c +418 -202
  342. data/vendor/libgit2/src/pack.h +25 -16
  343. data/vendor/libgit2/src/parse.c +124 -0
  344. data/vendor/libgit2/src/parse.h +61 -0
  345. data/vendor/libgit2/src/patch.c +223 -0
  346. data/vendor/libgit2/src/patch.h +68 -0
  347. data/vendor/libgit2/src/patch_generate.c +901 -0
  348. data/vendor/libgit2/src/patch_generate.h +69 -0
  349. data/vendor/libgit2/src/patch_parse.c +1136 -0
  350. data/vendor/libgit2/src/patch_parse.h +51 -0
  351. data/vendor/libgit2/src/path.c +1247 -241
  352. data/vendor/libgit2/src/path.h +353 -57
  353. data/vendor/libgit2/src/pathspec.c +586 -58
  354. data/vendor/libgit2/src/pathspec.h +37 -15
  355. data/vendor/libgit2/src/pool.c +134 -221
  356. data/vendor/libgit2/src/pool.h +38 -50
  357. data/vendor/libgit2/src/posix.c +76 -10
  358. data/vendor/libgit2/src/posix.h +74 -32
  359. data/vendor/libgit2/src/pqueue.c +79 -117
  360. data/vendor/libgit2/src/pqueue.h +38 -82
  361. data/vendor/libgit2/src/proxy.c +39 -0
  362. data/vendor/libgit2/src/proxy.h +17 -0
  363. data/vendor/libgit2/src/push.c +178 -279
  364. data/vendor/libgit2/src/push.h +93 -4
  365. data/vendor/libgit2/src/reader.c +265 -0
  366. data/vendor/libgit2/src/reader.h +107 -0
  367. data/vendor/libgit2/src/rebase.c +1364 -0
  368. data/vendor/libgit2/src/refdb.c +74 -19
  369. data/vendor/libgit2/src/refdb.h +16 -3
  370. data/vendor/libgit2/src/refdb_fs.c +1472 -603
  371. data/vendor/libgit2/src/refdb_fs.h +4 -0
  372. data/vendor/libgit2/src/reflog.c +40 -330
  373. data/vendor/libgit2/src/reflog.h +8 -2
  374. data/vendor/libgit2/src/refs.c +641 -225
  375. data/vendor/libgit2/src/refs.h +53 -6
  376. data/vendor/libgit2/src/refspec.c +175 -62
  377. data/vendor/libgit2/src/refspec.h +10 -25
  378. data/vendor/libgit2/src/remote.c +1741 -723
  379. data/vendor/libgit2/src/remote.h +17 -5
  380. data/vendor/libgit2/src/repository.c +1505 -421
  381. data/vendor/libgit2/src/repository.h +95 -15
  382. data/vendor/libgit2/src/reset.c +63 -26
  383. data/vendor/libgit2/src/revert.c +232 -0
  384. data/vendor/libgit2/src/revparse.c +94 -80
  385. data/vendor/libgit2/src/revwalk.c +427 -194
  386. data/vendor/libgit2/src/revwalk.h +14 -5
  387. data/vendor/libgit2/src/settings.c +290 -0
  388. data/vendor/libgit2/src/sha1_lookup.c +16 -159
  389. data/vendor/libgit2/src/sha1_lookup.h +5 -4
  390. data/vendor/libgit2/src/signature.c +138 -26
  391. data/vendor/libgit2/src/signature.h +5 -0
  392. data/vendor/libgit2/src/sortedcache.c +395 -0
  393. data/vendor/libgit2/src/sortedcache.h +180 -0
  394. data/vendor/libgit2/src/stash.c +629 -168
  395. data/vendor/libgit2/src/status.c +125 -75
  396. data/vendor/libgit2/src/status.h +4 -2
  397. data/vendor/libgit2/src/stdalloc.c +120 -0
  398. data/vendor/libgit2/src/stdalloc.h +17 -0
  399. data/vendor/libgit2/src/stream.h +86 -0
  400. data/vendor/libgit2/src/streams/mbedtls.c +483 -0
  401. data/vendor/libgit2/src/streams/mbedtls.h +23 -0
  402. data/vendor/libgit2/src/streams/openssl.c +789 -0
  403. data/vendor/libgit2/src/streams/openssl.h +23 -0
  404. data/vendor/libgit2/src/streams/registry.c +118 -0
  405. data/vendor/libgit2/src/streams/registry.h +19 -0
  406. data/vendor/libgit2/src/streams/socket.c +235 -0
  407. data/vendor/libgit2/src/streams/socket.h +23 -0
  408. data/vendor/libgit2/src/streams/stransport.c +323 -0
  409. data/vendor/libgit2/src/streams/stransport.h +21 -0
  410. data/vendor/libgit2/src/streams/tls.c +73 -0
  411. data/vendor/libgit2/src/streams/tls.h +31 -0
  412. data/vendor/libgit2/src/strmap.c +147 -0
  413. data/vendor/libgit2/src/strmap.h +46 -51
  414. data/vendor/libgit2/src/strnlen.h +24 -0
  415. data/vendor/libgit2/src/submodule.c +1633 -877
  416. data/vendor/libgit2/src/submodule.h +83 -21
  417. data/vendor/libgit2/src/sysdir.c +355 -0
  418. data/vendor/libgit2/src/sysdir.h +119 -0
  419. data/vendor/libgit2/src/tag.c +87 -62
  420. data/vendor/libgit2/src/tag.h +4 -1
  421. data/vendor/libgit2/src/thread-utils.c +3 -0
  422. data/vendor/libgit2/src/thread-utils.h +71 -35
  423. data/vendor/libgit2/src/trace.c +4 -4
  424. data/vendor/libgit2/src/trace.h +11 -3
  425. data/vendor/libgit2/src/trailer.c +416 -0
  426. data/vendor/libgit2/src/transaction.c +382 -0
  427. data/vendor/libgit2/src/transaction.h +14 -0
  428. data/vendor/libgit2/src/transport.c +133 -67
  429. data/vendor/libgit2/src/transports/auth.c +75 -0
  430. data/vendor/libgit2/src/transports/auth.h +64 -0
  431. data/vendor/libgit2/src/transports/auth_negotiate.c +277 -0
  432. data/vendor/libgit2/src/transports/auth_negotiate.h +27 -0
  433. data/vendor/libgit2/src/transports/cred.c +296 -68
  434. data/vendor/libgit2/src/transports/cred.h +16 -0
  435. data/vendor/libgit2/src/transports/cred_helpers.c +4 -0
  436. data/vendor/libgit2/src/transports/git.c +108 -90
  437. data/vendor/libgit2/src/transports/http.c +803 -258
  438. data/vendor/libgit2/src/transports/http.h +25 -0
  439. data/vendor/libgit2/src/transports/local.c +265 -169
  440. data/vendor/libgit2/src/transports/smart.c +255 -45
  441. data/vendor/libgit2/src/transports/smart.h +42 -22
  442. data/vendor/libgit2/src/transports/smart_pkt.c +250 -159
  443. data/vendor/libgit2/src/transports/smart_protocol.c +414 -196
  444. data/vendor/libgit2/src/transports/ssh.c +645 -236
  445. data/vendor/libgit2/src/transports/ssh.h +14 -0
  446. data/vendor/libgit2/src/transports/winhttp.c +809 -353
  447. data/vendor/libgit2/src/tree-cache.c +138 -52
  448. data/vendor/libgit2/src/tree-cache.h +14 -7
  449. data/vendor/libgit2/src/tree.c +620 -259
  450. data/vendor/libgit2/src/tree.h +12 -19
  451. data/vendor/libgit2/src/tsort.c +3 -2
  452. data/vendor/libgit2/src/unix/map.c +25 -7
  453. data/vendor/libgit2/src/unix/posix.h +77 -12
  454. data/vendor/libgit2/src/unix/pthread.h +56 -0
  455. data/vendor/libgit2/src/unix/realpath.c +12 -8
  456. data/vendor/libgit2/src/userdiff.h +208 -0
  457. data/vendor/libgit2/src/util.c +349 -165
  458. data/vendor/libgit2/src/util.h +167 -85
  459. data/vendor/libgit2/src/varint.c +43 -0
  460. data/vendor/libgit2/src/varint.h +17 -0
  461. data/vendor/libgit2/src/vector.c +156 -33
  462. data/vendor/libgit2/src/vector.h +41 -5
  463. data/vendor/libgit2/src/win32/dir.c +22 -42
  464. data/vendor/libgit2/src/win32/dir.h +7 -5
  465. data/vendor/libgit2/src/win32/error.c +6 -32
  466. data/vendor/libgit2/src/win32/error.h +4 -2
  467. data/vendor/libgit2/src/win32/findfile.c +62 -69
  468. data/vendor/libgit2/src/win32/findfile.h +5 -13
  469. data/vendor/libgit2/src/win32/git2.rc +44 -0
  470. data/vendor/libgit2/src/win32/map.c +39 -11
  471. data/vendor/libgit2/src/win32/mingw-compat.h +10 -11
  472. data/vendor/libgit2/src/win32/msvc-compat.h +10 -33
  473. data/vendor/libgit2/src/win32/path_w32.c +476 -0
  474. data/vendor/libgit2/src/win32/path_w32.h +104 -0
  475. data/vendor/libgit2/src/win32/posix.h +35 -30
  476. data/vendor/libgit2/src/win32/posix_w32.c +659 -327
  477. data/vendor/libgit2/src/win32/precompiled.h +7 -2
  478. data/vendor/libgit2/src/win32/reparse.h +57 -0
  479. data/vendor/libgit2/src/win32/thread.c +258 -0
  480. data/vendor/libgit2/src/win32/thread.h +64 -0
  481. data/vendor/libgit2/src/win32/utf-conv.c +127 -62
  482. data/vendor/libgit2/src/win32/utf-conv.h +47 -6
  483. data/vendor/libgit2/src/win32/version.h +21 -4
  484. data/vendor/libgit2/src/win32/w32_buffer.c +54 -0
  485. data/vendor/libgit2/src/win32/w32_buffer.h +20 -0
  486. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.c +438 -0
  487. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.h +129 -0
  488. data/vendor/libgit2/src/win32/w32_stack.c +193 -0
  489. data/vendor/libgit2/src/win32/w32_stack.h +140 -0
  490. data/vendor/libgit2/src/win32/w32_util.c +95 -0
  491. data/vendor/libgit2/src/win32/w32_util.h +170 -0
  492. data/vendor/libgit2/src/win32/win32-compat.h +52 -0
  493. data/vendor/libgit2/src/worktree.c +578 -0
  494. data/vendor/libgit2/src/worktree.h +39 -0
  495. data/vendor/libgit2/src/xdiff/xdiff.h +33 -18
  496. data/vendor/libgit2/src/xdiff/xdiffi.c +578 -88
  497. data/vendor/libgit2/src/xdiff/xdiffi.h +3 -2
  498. data/vendor/libgit2/src/xdiff/xemit.c +106 -45
  499. data/vendor/libgit2/src/xdiff/xemit.h +3 -3
  500. data/vendor/libgit2/src/xdiff/xhistogram.c +5 -4
  501. data/vendor/libgit2/src/xdiff/xinclude.h +3 -2
  502. data/vendor/libgit2/src/xdiff/xmacros.h +2 -2
  503. data/vendor/libgit2/src/xdiff/xmerge.c +167 -48
  504. data/vendor/libgit2/src/xdiff/xpatience.c +42 -10
  505. data/vendor/libgit2/src/xdiff/xprepare.c +14 -14
  506. data/vendor/libgit2/src/xdiff/xprepare.h +2 -2
  507. data/vendor/libgit2/src/xdiff/xtypes.h +2 -2
  508. data/vendor/libgit2/src/xdiff/xutils.c +60 -56
  509. data/vendor/libgit2/src/xdiff/xutils.h +3 -5
  510. data/vendor/libgit2/src/zstream.c +205 -0
  511. data/vendor/libgit2/src/zstream.h +53 -0
  512. metadata +281 -233
  513. data/Rakefile +0 -61
  514. data/ext/rugged/rugged_diff_patch.c +0 -169
  515. data/lib/rugged/diff/patch.rb +0 -28
  516. data/test/blob_test.rb +0 -341
  517. data/test/branch_test.rb +0 -199
  518. data/test/commit_test.rb +0 -104
  519. data/test/config_test.rb +0 -45
  520. data/test/coverage/cover.rb +0 -133
  521. data/test/diff_test.rb +0 -777
  522. data/test/errors_test.rb +0 -34
  523. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  524. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  525. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  526. data/test/fixtures/archive.tar.gz +0 -0
  527. data/test/fixtures/attr/attr0 +0 -1
  528. data/test/fixtures/attr/attr1 +0 -29
  529. data/test/fixtures/attr/attr2 +0 -21
  530. data/test/fixtures/attr/attr3 +0 -4
  531. data/test/fixtures/attr/binfile +0 -1
  532. data/test/fixtures/attr/dir/file +0 -0
  533. data/test/fixtures/attr/file +0 -1
  534. data/test/fixtures/attr/gitattributes +0 -29
  535. data/test/fixtures/attr/gitignore +0 -2
  536. data/test/fixtures/attr/ign +0 -1
  537. data/test/fixtures/attr/macro_bad +0 -1
  538. data/test/fixtures/attr/macro_test +0 -1
  539. data/test/fixtures/attr/root_test1 +0 -1
  540. data/test/fixtures/attr/root_test2 +0 -6
  541. data/test/fixtures/attr/root_test3 +0 -19
  542. data/test/fixtures/attr/root_test4.txt +0 -14
  543. data/test/fixtures/attr/sub/abc +0 -37
  544. data/test/fixtures/attr/sub/dir/file +0 -0
  545. data/test/fixtures/attr/sub/file +0 -1
  546. data/test/fixtures/attr/sub/ign/file +0 -1
  547. data/test/fixtures/attr/sub/ign/sub/file +0 -1
  548. data/test/fixtures/attr/sub/sub/dir +0 -0
  549. data/test/fixtures/attr/sub/sub/file +0 -1
  550. data/test/fixtures/attr/sub/sub/subsub.txt +0 -1
  551. data/test/fixtures/attr/sub/subdir_test1 +0 -2
  552. data/test/fixtures/attr/sub/subdir_test2.txt +0 -1
  553. data/test/fixtures/diff/another.txt +0 -38
  554. data/test/fixtures/diff/readme.txt +0 -36
  555. data/test/fixtures/mergedrepo/conflicts-one.txt +0 -5
  556. data/test/fixtures/mergedrepo/conflicts-two.txt +0 -5
  557. data/test/fixtures/mergedrepo/one.txt +0 -10
  558. data/test/fixtures/mergedrepo/two.txt +0 -12
  559. data/test/fixtures/status/current_file +0 -1
  560. data/test/fixtures/status/ignored_file +0 -1
  561. data/test/fixtures/status/modified_file +0 -2
  562. data/test/fixtures/status/new_file +0 -1
  563. data/test/fixtures/status/staged_changes +0 -2
  564. data/test/fixtures/status/staged_changes_modified_file +0 -3
  565. data/test/fixtures/status/staged_delete_modified_file +0 -1
  566. data/test/fixtures/status/staged_new_file +0 -1
  567. data/test/fixtures/status/staged_new_file_modified_file +0 -2
  568. data/test/fixtures/status/subdir/current_file +0 -1
  569. data/test/fixtures/status/subdir/modified_file +0 -2
  570. data/test/fixtures/status/subdir/new_file +0 -1
  571. data/test/fixtures/status/subdir.txt +0 -2
  572. data/test/fixtures/status//350/277/231 +0 -1
  573. data/test/fixtures/testrepo.git/HEAD +0 -1
  574. data/test/fixtures/testrepo.git/config +0 -13
  575. data/test/fixtures/testrepo.git/description +0 -1
  576. data/test/fixtures/testrepo.git/index +0 -0
  577. data/test/fixtures/testrepo.git/info/exclude +0 -6
  578. data/test/fixtures/testrepo.git/logs/HEAD +0 -3
  579. data/test/fixtures/testrepo.git/logs/refs/heads/master +0 -3
  580. data/test/fixtures/testrepo.git/logs/refs/notes/commits +0 -1
  581. data/test/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d +0 -0
  582. data/test/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 +0 -0
  583. data/test/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 +0 -0
  584. data/test/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd +0 -0
  585. data/test/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 +0 -0
  586. data/test/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a +0 -2
  587. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +0 -1
  588. data/test/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 +0 -0
  589. data/test/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 +0 -2
  590. data/test/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 +0 -2
  591. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  592. data/test/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 +0 -0
  593. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +0 -1
  594. data/test/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a +0 -0
  595. data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
  596. data/test/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d +0 -0
  597. data/test/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 +0 -0
  598. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  599. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  600. data/test/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a +0 -3
  601. data/test/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f +0 -2
  602. data/test/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd +0 -0
  603. data/test/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 +0 -0
  604. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +0 -3
  605. data/test/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 +0 -3
  606. data/test/fixtures/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd +0 -3
  607. data/test/fixtures/testrepo.git/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b +0 -0
  608. data/test/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  609. data/test/fixtures/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 +0 -0
  610. data/test/fixtures/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 +0 -0
  611. data/test/fixtures/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 +0 -0
  612. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx +0 -0
  613. data/test/fixtures/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack +0 -0
  614. data/test/fixtures/testrepo.git/packed-refs +0 -2
  615. data/test/fixtures/testrepo.git/refs/heads/master +0 -1
  616. data/test/fixtures/testrepo.git/refs/notes/commits +0 -1
  617. data/test/fixtures/testrepo.git/refs/tags/v0.9 +0 -1
  618. data/test/fixtures/testrepo.git/refs/tags/v1.0 +0 -1
  619. data/test/fixtures/text_file.md +0 -464
  620. data/test/fixtures/unsymlinked.git/HEAD +0 -1
  621. data/test/fixtures/unsymlinked.git/config +0 -6
  622. data/test/fixtures/unsymlinked.git/description +0 -1
  623. data/test/fixtures/unsymlinked.git/info/exclude +0 -2
  624. data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
  625. data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
  626. data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
  627. data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
  628. data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
  629. data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
  630. data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
  631. data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
  632. data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
  633. data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +0 -2
  634. data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
  635. data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
  636. data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
  637. data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
  638. data/test/fixtures/unsymlinked.git/refs/heads/exe-file +0 -1
  639. data/test/fixtures/unsymlinked.git/refs/heads/master +0 -1
  640. data/test/fixtures/unsymlinked.git/refs/heads/reg-file +0 -1
  641. data/test/index_test.rb +0 -333
  642. data/test/lib_test.rb +0 -127
  643. data/test/note_test.rb +0 -158
  644. data/test/object_test.rb +0 -43
  645. data/test/reference_test.rb +0 -207
  646. data/test/remote_test.rb +0 -324
  647. data/test/repo_pack_test.rb +0 -24
  648. data/test/repo_reset_test.rb +0 -82
  649. data/test/repo_test.rb +0 -402
  650. data/test/tag_test.rb +0 -68
  651. data/test/test_helper.rb +0 -92
  652. data/test/tree_test.rb +0 -91
  653. data/test/walker_test.rb +0 -88
  654. data/vendor/libgit2/Makefile.embed +0 -42
  655. data/vendor/libgit2/include/git2/push.h +0 -131
  656. data/vendor/libgit2/include/git2/threads.h +0 -50
  657. data/vendor/libgit2/src/amiga/map.c +0 -48
  658. data/vendor/libgit2/src/bswap.h +0 -97
  659. data/vendor/libgit2/src/compress.c +0 -53
  660. data/vendor/libgit2/src/config_file.h +0 -60
  661. data/vendor/libgit2/src/delta-apply.c +0 -134
  662. data/vendor/libgit2/src/delta-apply.h +0 -50
  663. data/vendor/libgit2/src/diff_patch.c +0 -995
  664. data/vendor/libgit2/src/diff_patch.h +0 -46
  665. data/vendor/libgit2/src/hashsig.h +0 -72
  666. data/vendor/libgit2/src/merge_file.h +0 -71
  667. data/vendor/libgit2/src/win32/pthread.c +0 -144
  668. data/vendor/libgit2/src/win32/pthread.h +0 -50
@@ -6,137 +6,376 @@
6
6
  */
7
7
 
8
8
  #include "iterator.h"
9
+
9
10
  #include "tree.h"
10
11
  #include "index.h"
11
- #include "ignore.h"
12
- #include "buffer.h"
13
- #include "git2/submodule.h"
14
- #include <ctype.h>
15
-
16
- #define ITERATOR_SET_CB(P,NAME_LC) do { \
17
- (P)->cb.current = NAME_LC ## _iterator__current; \
18
- (P)->cb.advance = NAME_LC ## _iterator__advance; \
19
- (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
20
- (P)->cb.seek = NAME_LC ## _iterator__seek; \
21
- (P)->cb.reset = NAME_LC ## _iterator__reset; \
22
- (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
23
- (P)->cb.free = NAME_LC ## _iterator__free; \
24
- } while (0)
25
-
26
- #define ITERATOR_CASE_FLAGS \
27
- (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
28
-
29
- #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
30
- (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
31
- (P)->base.cb = &(P)->cb; \
32
- ITERATOR_SET_CB(P,NAME_LC); \
33
- (P)->base.repo = (REPO); \
34
- (P)->base.start = start ? git__strdup(start) : NULL; \
35
- (P)->base.end = end ? git__strdup(end) : NULL; \
36
- if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
37
- git__free(P); return -1; } \
38
- (P)->base.prefixcomp = git__prefixcmp; \
39
- (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
40
- if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
41
- (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
42
- } while (0)
43
12
 
44
- #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
45
- #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
46
- #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
47
- #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
48
- #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
13
+ #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
14
+ #define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
15
+ #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
49
16
 
50
- #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
17
+ #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
18
+ #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
19
+ #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
20
+ #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
21
+ #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
22
+ #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
51
23
  #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
24
+ #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
25
+ #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
26
+ #define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS)
27
+
52
28
 
53
- #define iterator__end(I) ((git_iterator *)(I))->end
54
- #define iterator__past_end(I,PATH) \
55
- (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
29
+ static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
30
+ {
31
+ if (ignore_case)
32
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
33
+ else
34
+ iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
56
35
 
36
+ iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp;
37
+ iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp;
38
+ iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
39
+ iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
40
+
41
+ git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
42
+ }
57
43
 
58
- static int iterator__reset_range(
44
+ static int iterator_range_init(
59
45
  git_iterator *iter, const char *start, const char *end)
60
46
  {
61
- if (start) {
62
- if (iter->start)
63
- git__free(iter->start);
47
+ if (start && *start) {
64
48
  iter->start = git__strdup(start);
65
- GITERR_CHECK_ALLOC(iter->start);
49
+ GIT_ERROR_CHECK_ALLOC(iter->start);
50
+
51
+ iter->start_len = strlen(iter->start);
66
52
  }
67
53
 
68
- if (end) {
69
- if (iter->end)
70
- git__free(iter->end);
54
+ if (end && *end) {
71
55
  iter->end = git__strdup(end);
72
- GITERR_CHECK_ALLOC(iter->end);
56
+ GIT_ERROR_CHECK_ALLOC(iter->end);
57
+
58
+ iter->end_len = strlen(iter->end);
73
59
  }
74
60
 
75
- iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
61
+ iter->started = (iter->start == NULL);
62
+ iter->ended = false;
63
+
64
+ return 0;
65
+ }
66
+
67
+ static void iterator_range_free(git_iterator *iter)
68
+ {
69
+ if (iter->start) {
70
+ git__free(iter->start);
71
+ iter->start = NULL;
72
+ iter->start_len = 0;
73
+ }
74
+
75
+ if (iter->end) {
76
+ git__free(iter->end);
77
+ iter->end = NULL;
78
+ iter->end_len = 0;
79
+ }
80
+ }
81
+
82
+ static int iterator_reset_range(
83
+ git_iterator *iter, const char *start, const char *end)
84
+ {
85
+ iterator_range_free(iter);
86
+ return iterator_range_init(iter, start, end);
87
+ }
88
+
89
+ static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist)
90
+ {
91
+ size_t i;
92
+
93
+ if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0)
94
+ return -1;
95
+
96
+ for (i = 0; i < pathlist->count; i++) {
97
+ if (!pathlist->strings[i])
98
+ continue;
99
+
100
+ if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0)
101
+ return -1;
102
+ }
76
103
 
77
104
  return 0;
78
105
  }
79
106
 
80
- static int iterator__update_ignore_case(
107
+ static int iterator_init_common(
81
108
  git_iterator *iter,
82
- git_iterator_flag_t flags)
109
+ git_repository *repo,
110
+ git_index *index,
111
+ git_iterator_options *given_opts)
83
112
  {
84
- int error = 0, ignore_case = -1;
113
+ static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT;
114
+ git_iterator_options *options = given_opts ? given_opts : &default_opts;
115
+ bool ignore_case;
116
+ int precompose;
117
+ int error;
118
+
119
+ iter->repo = repo;
120
+ iter->index = index;
121
+ iter->flags = options->flags;
85
122
 
86
- if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
123
+ if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
87
124
  ignore_case = true;
88
- else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
125
+ } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) {
89
126
  ignore_case = false;
90
- else {
127
+ } else if (repo) {
91
128
  git_index *index;
92
129
 
93
- if (!(error = git_repository_index__weakptr(&index, iter->repo)))
94
- ignore_case = (index->ignore_case != false);
130
+ if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
131
+ return error;
132
+
133
+ ignore_case = !!index->ignore_case;
134
+
135
+ if (ignore_case == 1)
136
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
137
+ else
138
+ iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
139
+ } else {
140
+ ignore_case = false;
141
+ }
142
+
143
+ /* try to look up precompose and set flag if appropriate */
144
+ if (repo &&
145
+ (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 &&
146
+ (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) {
147
+
148
+ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
149
+ git_error_clear();
150
+ else if (precompose)
151
+ iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
95
152
  }
96
153
 
97
- if (ignore_case > 0)
98
- iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
99
- else if (ignore_case == 0)
100
- iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
154
+ if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND))
155
+ iter->flags |= GIT_ITERATOR_INCLUDE_TREES;
101
156
 
102
- iter->prefixcomp = iterator__ignore_case(iter) ?
103
- git__prefixcmp_icase : git__prefixcmp;
157
+ if ((error = iterator_range_init(iter, options->start, options->end)) < 0 ||
158
+ (error = iterator_pathlist_init(iter, &options->pathlist)) < 0)
159
+ return error;
104
160
 
105
- return error;
161
+ iterator_set_ignore_case(iter, ignore_case);
162
+ return 0;
106
163
  }
107
164
 
108
- GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
165
+ static void iterator_clear(git_iterator *iter)
109
166
  {
110
- if (entry) *entry = NULL;
167
+ iter->started = false;
168
+ iter->ended = false;
169
+ iter->stat_calls = 0;
170
+ iter->pathlist_walk_idx = 0;
171
+ iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
111
172
  }
112
173
 
174
+ GIT_INLINE(bool) iterator_has_started(
175
+ git_iterator *iter, const char *path, bool is_submodule)
176
+ {
177
+ size_t path_len;
178
+
179
+ if (iter->start == NULL || iter->started == true)
180
+ return true;
181
+
182
+ /* the starting path is generally a prefix - we have started once we
183
+ * are prefixed by this path
184
+ */
185
+ iter->started = (iter->prefixcomp(path, iter->start) >= 0);
186
+
187
+ if (iter->started)
188
+ return true;
189
+
190
+ path_len = strlen(path);
113
191
 
114
- static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
192
+ /* if, however, we are a submodule, then we support `start` being
193
+ * suffixed with a `/` for crazy legacy reasons. match `submod`
194
+ * with a start path of `submod/`.
195
+ */
196
+ if (is_submodule && iter->start_len && path_len == iter->start_len - 1 &&
197
+ iter->start[iter->start_len-1] == '/')
198
+ return true;
199
+
200
+ /* if, however, our current path is a directory, and our starting path
201
+ * is _beneath_ that directory, then recurse into the directory (even
202
+ * though we have not yet "started")
203
+ */
204
+ if (path_len > 0 && path[path_len-1] == '/' &&
205
+ iter->strncomp(path, iter->start, path_len) == 0)
206
+ return true;
207
+
208
+ return false;
209
+ }
210
+
211
+ GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path)
115
212
  {
116
- GIT_UNUSED(i);
117
- iterator__clear_entry(e);
118
- return GIT_ITEROVER;
213
+ if (iter->end == NULL)
214
+ return false;
215
+ else if (iter->ended)
216
+ return true;
217
+
218
+ iter->ended = (iter->prefixcomp(path, iter->end) > 0);
219
+ return iter->ended;
220
+ }
221
+
222
+ /* walker for the index and tree iterator that allows it to walk the sorted
223
+ * pathlist entries alongside sorted iterator entries.
224
+ */
225
+ static bool iterator_pathlist_next_is(git_iterator *iter, const char *path)
226
+ {
227
+ char *p;
228
+ size_t path_len, p_len, cmp_len, i;
229
+ int cmp;
230
+
231
+ if (iter->pathlist.length == 0)
232
+ return true;
233
+
234
+ git_vector_sort(&iter->pathlist);
235
+
236
+ path_len = strlen(path);
237
+
238
+ /* for comparison, drop the trailing slash on the current '/' */
239
+ if (path_len && path[path_len-1] == '/')
240
+ path_len--;
241
+
242
+ for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
243
+ p = iter->pathlist.contents[i];
244
+ p_len = strlen(p);
245
+
246
+ if (p_len && p[p_len-1] == '/')
247
+ p_len--;
248
+
249
+ cmp_len = min(path_len, p_len);
250
+
251
+ /* see if the pathlist entry is a prefix of this path */
252
+ cmp = iter->strncomp(p, path, cmp_len);
253
+
254
+ /* prefix match - see if there's an exact match, or if we were
255
+ * given a path that matches the directory
256
+ */
257
+ if (cmp == 0) {
258
+ /* if this pathlist entry is not suffixed with a '/' then
259
+ * it matches a path that is a file or a directory.
260
+ * (eg, pathlist = "foo" and path is "foo" or "foo/" or
261
+ * "foo/something")
262
+ */
263
+ if (p[cmp_len] == '\0' &&
264
+ (path[cmp_len] == '\0' || path[cmp_len] == '/'))
265
+ return true;
266
+
267
+ /* if this pathlist entry _is_ suffixed with a '/' then
268
+ * it matches only paths that are directories.
269
+ * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
270
+ */
271
+ if (p[cmp_len] == '/' && path[cmp_len] == '/')
272
+ return true;
273
+ }
274
+
275
+ /* this pathlist entry sorts before the given path, try the next */
276
+ else if (cmp < 0) {
277
+ iter->pathlist_walk_idx++;
278
+ continue;
279
+ }
280
+
281
+ /* this pathlist sorts after the given path, no match. */
282
+ else if (cmp > 0) {
283
+ break;
284
+ }
285
+ }
286
+
287
+ return false;
288
+ }
289
+
290
+ typedef enum {
291
+ ITERATOR_PATHLIST_NONE = 0,
292
+ ITERATOR_PATHLIST_IS_FILE = 1,
293
+ ITERATOR_PATHLIST_IS_DIR = 2,
294
+ ITERATOR_PATHLIST_IS_PARENT = 3,
295
+ ITERATOR_PATHLIST_FULL = 4,
296
+ } iterator_pathlist_search_t;
297
+
298
+ static iterator_pathlist_search_t iterator_pathlist_search(
299
+ git_iterator *iter, const char *path, size_t path_len)
300
+ {
301
+ const char *p;
302
+ size_t idx;
303
+ int error;
304
+
305
+ if (iter->pathlist.length == 0)
306
+ return ITERATOR_PATHLIST_FULL;
307
+
308
+ git_vector_sort(&iter->pathlist);
309
+
310
+ error = git_vector_bsearch2(&idx, &iter->pathlist,
311
+ (git_vector_cmp)iter->strcomp, path);
312
+
313
+ /* the given path was found in the pathlist. since the pathlist only
314
+ * matches directories when they're suffixed with a '/', analyze the
315
+ * path string to determine whether it's a directory or not.
316
+ */
317
+ if (error == 0) {
318
+ if (path_len && path[path_len-1] == '/')
319
+ return ITERATOR_PATHLIST_IS_DIR;
320
+
321
+ return ITERATOR_PATHLIST_IS_FILE;
322
+ }
323
+
324
+ /* at this point, the path we're examining may be a directory (though we
325
+ * don't know that yet, since we're avoiding a stat unless it's necessary)
326
+ * so walk the pathlist looking for the given path with a '/' after it,
327
+ */
328
+ while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
329
+ if (iter->prefixcomp(p, path) != 0)
330
+ break;
331
+
332
+ /* an exact match would have been matched by the bsearch above */
333
+ assert(p[path_len]);
334
+
335
+ /* is this a literal directory entry (eg `foo/`) or a file beneath */
336
+ if (p[path_len] == '/') {
337
+ return (p[path_len+1] == '\0') ?
338
+ ITERATOR_PATHLIST_IS_DIR :
339
+ ITERATOR_PATHLIST_IS_PARENT;
340
+ }
341
+
342
+ if (p[path_len] > '/')
343
+ break;
344
+
345
+ idx++;
346
+ }
347
+
348
+ return ITERATOR_PATHLIST_NONE;
119
349
  }
120
350
 
121
- static int empty_iterator__seek(git_iterator *i, const char *p)
351
+ /* Empty iterator */
352
+
353
+ static int empty_iterator_noop(const git_index_entry **e, git_iterator *i)
122
354
  {
123
- GIT_UNUSED(i); GIT_UNUSED(p);
124
- return -1;
355
+ GIT_UNUSED(i);
356
+
357
+ if (e)
358
+ *e = NULL;
359
+
360
+ return GIT_ITEROVER;
125
361
  }
126
362
 
127
- static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
363
+ static int empty_iterator_advance_over(
364
+ const git_index_entry **e,
365
+ git_iterator_status_t *s,
366
+ git_iterator *i)
128
367
  {
129
- GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
130
- return 0;
368
+ *s = GIT_ITERATOR_STATUS_EMPTY;
369
+ return empty_iterator_noop(e, i);
131
370
  }
132
371
 
133
- static int empty_iterator__at_end(git_iterator *i)
372
+ static int empty_iterator_reset(git_iterator *i)
134
373
  {
135
374
  GIT_UNUSED(i);
136
- return 1;
375
+ return 0;
137
376
  }
138
377
 
139
- static void empty_iterator__free(git_iterator *i)
378
+ static void empty_iterator_free(git_iterator *i)
140
379
  {
141
380
  GIT_UNUSED(i);
142
381
  }
@@ -147,1347 +386,2029 @@ typedef struct {
147
386
  } empty_iterator;
148
387
 
149
388
  int git_iterator_for_nothing(
150
- git_iterator **iter,
151
- git_iterator_flag_t flags,
152
- const char *start,
153
- const char *end)
389
+ git_iterator **out,
390
+ git_iterator_options *options)
154
391
  {
155
- empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
156
- GITERR_CHECK_ALLOC(i);
392
+ empty_iterator *iter;
157
393
 
158
- #define empty_iterator__current empty_iterator__noop
159
- #define empty_iterator__advance empty_iterator__noop
160
- #define empty_iterator__advance_into empty_iterator__noop
394
+ static git_iterator_callbacks callbacks = {
395
+ empty_iterator_noop,
396
+ empty_iterator_noop,
397
+ empty_iterator_noop,
398
+ empty_iterator_advance_over,
399
+ empty_iterator_reset,
400
+ empty_iterator_free
401
+ };
161
402
 
162
- ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
403
+ *out = NULL;
163
404
 
164
- if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
165
- i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
405
+ iter = git__calloc(1, sizeof(empty_iterator));
406
+ GIT_ERROR_CHECK_ALLOC(iter);
166
407
 
167
- *iter = (git_iterator *)i;
408
+ iter->base.type = GIT_ITERATOR_TYPE_EMPTY;
409
+ iter->base.cb = &callbacks;
410
+ iter->base.flags = options->flags;
411
+
412
+ *out = &iter->base;
168
413
  return 0;
169
414
  }
170
415
 
416
+ /* Tree iterator */
417
+
418
+ typedef struct {
419
+ git_tree_entry *tree_entry;
420
+ const char *parent_path;
421
+ } tree_iterator_entry;
171
422
 
172
- typedef struct tree_iterator_entry tree_iterator_entry;
173
- struct tree_iterator_entry {
174
- tree_iterator_entry *parent;
175
- const git_tree_entry *te;
423
+ typedef struct {
176
424
  git_tree *tree;
177
- };
178
425
 
179
- typedef struct tree_iterator_frame tree_iterator_frame;
180
- struct tree_iterator_frame {
181
- tree_iterator_frame *up, *down;
426
+ /* path to this particular frame (folder) */
427
+ git_buf path;
182
428
 
183
- size_t n_entries; /* items in this frame */
184
- size_t current; /* start of currently active range in frame */
185
- size_t next; /* start of next range in frame */
429
+ /* a sorted list of the entries for this frame (folder), these are
430
+ * actually pointers to the iterator's entry pool.
431
+ */
432
+ git_vector entries;
433
+ tree_iterator_entry *current;
186
434
 
187
- const char *start;
188
- size_t startlen;
435
+ size_t next_idx;
189
436
 
190
- tree_iterator_entry *entries[GIT_FLEX_ARRAY];
191
- };
437
+ /* on case insensitive iterations, we also have an array of other
438
+ * paths that were case insensitively equal to this one, and their
439
+ * tree objects. we have coalesced the tree entries into this frame.
440
+ * a child `tree_iterator_entry` will contain a pointer to its actual
441
+ * parent path.
442
+ */
443
+ git_vector similar_trees;
444
+ git_array_t(git_buf) similar_paths;
445
+ } tree_iterator_frame;
192
446
 
193
447
  typedef struct {
194
448
  git_iterator base;
195
- git_iterator_callbacks cb;
196
- tree_iterator_frame *head, *root;
197
- git_pool pool;
449
+ git_tree *root;
450
+ git_array_t(tree_iterator_frame) frames;
451
+
198
452
  git_index_entry entry;
199
- git_buf path;
200
- int path_ambiguities;
201
- bool path_has_filename;
202
- bool entry_is_current;
203
- int (*strncomp)(const char *a, const char *b, size_t sz);
453
+ git_buf entry_path;
454
+
455
+ /* a pool of entries to reduce the number of allocations */
456
+ git_pool entry_pool;
204
457
  } tree_iterator;
205
458
 
206
- static char *tree_iterator__current_filename(
207
- tree_iterator *ti, const git_tree_entry *te)
459
+ GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame(
460
+ tree_iterator *iter)
208
461
  {
209
- if (!ti->path_has_filename) {
210
- if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
211
- return NULL;
212
-
213
- if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
214
- return NULL;
215
-
216
- ti->path_has_filename = true;
217
- }
218
-
219
- return ti->path.ptr;
462
+ return iter->frames.size > 1 ?
463
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
220
464
  }
221
465
 
222
- static void tree_iterator__rewrite_filename(tree_iterator *ti)
466
+ GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame(
467
+ tree_iterator *iter)
223
468
  {
224
- tree_iterator_entry *scan = ti->head->entries[ti->head->current];
225
- ssize_t strpos = ti->path.size;
226
- const git_tree_entry *te;
227
-
228
- if (strpos && ti->path.ptr[strpos - 1] == '/')
229
- strpos--;
230
-
231
- for (; scan && (te = scan->te); scan = scan->parent) {
232
- strpos -= te->filename_len;
233
- memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
234
- strpos -= 1; /* separator */
235
- }
469
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
236
470
  }
237
471
 
238
- static int tree_iterator__te_cmp(
239
- const git_tree_entry *a,
240
- const git_tree_entry *b,
241
- int (*compare)(const char *, const char *, size_t))
472
+ GIT_INLINE(int) tree_entry_cmp(
473
+ const git_tree_entry *a, const git_tree_entry *b, bool icase)
242
474
  {
243
475
  return git_path_cmp(
244
476
  a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
245
477
  b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
246
- compare);
478
+ icase ? git__strncasecmp : git__strncmp);
247
479
  }
248
480
 
249
- static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
481
+ GIT_INLINE(int) tree_iterator_entry_cmp_icase(
482
+ const void *ptr_a, const void *ptr_b)
250
483
  {
251
- const tree_iterator_entry *ae = a, *be = b;
252
- int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
253
-
254
- if (!cmp) {
255
- /* stabilize sort order among equivalent names */
256
- if (!ae->parent->te || !be->parent->te)
257
- cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
258
- else
259
- cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
260
- }
484
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
485
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
261
486
 
262
- return cmp;
487
+ return tree_entry_cmp(a->tree_entry, b->tree_entry, true);
263
488
  }
264
489
 
265
- static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
490
+ static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b)
266
491
  {
267
- const tree_iterator_frame *tf = key;
268
- const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
492
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
493
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
269
494
 
270
- return git_path_cmp(
271
- tf->start, tf->startlen, false,
272
- te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
273
- ((tree_iterator *)p)->strncomp);
495
+ int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true);
496
+
497
+ /* stabilize the sort order for filenames that are (case insensitively)
498
+ * the same by examining the parent path (case sensitively) before
499
+ * falling back to a case sensitive sort of the filename.
500
+ */
501
+ if (!c && a->parent_path != b->parent_path)
502
+ c = git__strcmp(a->parent_path, b->parent_path);
503
+
504
+ if (!c)
505
+ c = tree_entry_cmp(a->tree_entry, b->tree_entry, false);
506
+
507
+ return c;
274
508
  }
275
509
 
276
- static bool tree_iterator__move_to_next(
277
- tree_iterator *ti, tree_iterator_frame *tf)
510
+ static int tree_iterator_compute_path(
511
+ git_buf *out,
512
+ tree_iterator_entry *entry)
278
513
  {
279
- if (tf->next > tf->current + 1)
280
- ti->path_ambiguities--;
514
+ git_buf_clear(out);
281
515
 
282
- if (!tf->up) { /* at root */
283
- tf->current = tf->next;
284
- return false;
285
- }
516
+ if (entry->parent_path)
517
+ git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename);
518
+ else
519
+ git_buf_puts(out, entry->tree_entry->filename);
286
520
 
287
- for (; tf->current < tf->next; tf->current++) {
288
- git_tree_free(tf->entries[tf->current]->tree);
289
- tf->entries[tf->current]->tree = NULL;
290
- }
521
+ if (git_tree_entry__is_tree(entry->tree_entry))
522
+ git_buf_putc(out, '/');
523
+
524
+ if (git_buf_oom(out))
525
+ return -1;
291
526
 
292
- return (tf->current < tf->n_entries);
527
+ return 0;
293
528
  }
294
529
 
295
- static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
530
+ static int tree_iterator_frame_init(
531
+ tree_iterator *iter,
532
+ git_tree *tree,
533
+ tree_iterator_entry *frame_entry)
296
534
  {
535
+ tree_iterator_frame *new_frame = NULL;
536
+ tree_iterator_entry *new_entry;
537
+ git_tree *dup = NULL;
538
+ git_tree_entry *tree_entry;
539
+ git_vector_cmp cmp;
540
+ size_t i;
297
541
  int error = 0;
298
- const git_tree_entry *te, *last = NULL;
299
542
 
300
- tf->next = tf->current;
543
+ new_frame = git_array_alloc(iter->frames);
544
+ GIT_ERROR_CHECK_ALLOC(new_frame);
301
545
 
302
- for (; tf->next < tf->n_entries; tf->next++, last = te) {
303
- te = tf->entries[tf->next]->te;
546
+ memset(new_frame, 0, sizeof(tree_iterator_frame));
304
547
 
305
- if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
306
- break;
548
+ if ((error = git_tree_dup(&dup, tree)) < 0)
549
+ goto done;
550
+
551
+ memset(new_frame, 0x0, sizeof(tree_iterator_frame));
552
+ new_frame->tree = dup;
553
+
554
+ if (frame_entry &&
555
+ (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0)
556
+ goto done;
557
+
558
+ cmp = iterator__ignore_case(&iter->base) ?
559
+ tree_iterator_entry_sort_icase : NULL;
307
560
 
308
- /* try to load trees for items in [current,next) range */
309
- if (!error && git_tree_entry__is_tree(te))
310
- error = git_tree_lookup(
311
- &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
561
+ if ((error = git_vector_init(
562
+ &new_frame->entries, dup->entries.size, cmp)) < 0)
563
+ goto done;
564
+
565
+ git_array_foreach(dup->entries, i, tree_entry) {
566
+ new_entry = git_pool_malloc(&iter->entry_pool, 1);
567
+ GIT_ERROR_CHECK_ALLOC(new_entry);
568
+
569
+ new_entry->tree_entry = tree_entry;
570
+ new_entry->parent_path = new_frame->path.ptr;
571
+
572
+ if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0)
573
+ goto done;
312
574
  }
313
575
 
314
- if (tf->next > tf->current + 1)
315
- ti->path_ambiguities++;
576
+ git_vector_set_sorted(&new_frame->entries,
577
+ !iterator__ignore_case(&iter->base));
316
578
 
317
- /* if a tree lookup failed, advance over this span and return failure */
579
+ done:
318
580
  if (error < 0) {
319
- tree_iterator__move_to_next(ti, tf);
320
- return error;
581
+ git_tree_free(dup);
582
+ git_array_pop(iter->frames);
321
583
  }
322
584
 
323
- if (last && !tree_iterator__current_filename(ti, last))
324
- return -1; /* must have been allocation failure */
325
-
326
- return 0;
585
+ return error;
327
586
  }
328
587
 
329
- GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
588
+ GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry(
589
+ tree_iterator_frame *frame)
330
590
  {
331
- return (ti->head->current < ti->head->n_entries &&
332
- ti->head->entries[ti->head->current]->tree != NULL);
591
+ return frame->current;
333
592
  }
334
593
 
335
- static int tree_iterator__push_frame(tree_iterator *ti)
594
+ GIT_INLINE(int) tree_iterator_frame_push_neighbors(
595
+ tree_iterator *iter,
596
+ tree_iterator_frame *parent_frame,
597
+ tree_iterator_frame *frame,
598
+ const char *filename)
336
599
  {
600
+ tree_iterator_entry *entry, *new_entry;
601
+ git_tree *tree = NULL;
602
+ git_tree_entry *tree_entry;
603
+ git_buf *path;
604
+ size_t new_size, i;
337
605
  int error = 0;
338
- tree_iterator_frame *head = ti->head, *tf = NULL;
339
- size_t i, n_entries = 0;
340
606
 
341
- if (head->current >= head->n_entries || !head->entries[head->current]->tree)
342
- return GIT_ITEROVER;
607
+ while (parent_frame->next_idx < parent_frame->entries.length) {
608
+ entry = parent_frame->entries.contents[parent_frame->next_idx];
609
+
610
+ if (strcasecmp(filename, entry->tree_entry->filename) != 0)
611
+ break;
612
+
613
+ if ((error = git_tree_lookup(&tree,
614
+ iter->base.repo, entry->tree_entry->oid)) < 0)
615
+ break;
343
616
 
344
- for (i = head->current; i < head->next; ++i)
345
- n_entries += git_tree_entrycount(head->entries[i]->tree);
617
+ if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
618
+ break;
346
619
 
347
- tf = git__calloc(sizeof(tree_iterator_frame) +
348
- n_entries * sizeof(tree_iterator_entry *), 1);
349
- GITERR_CHECK_ALLOC(tf);
620
+ path = git_array_alloc(parent_frame->similar_paths);
621
+ GIT_ERROR_CHECK_ALLOC(path);
350
622
 
351
- tf->n_entries = n_entries;
623
+ memset(path, 0, sizeof(git_buf));
352
624
 
353
- tf->up = head;
354
- head->down = tf;
355
- ti->head = tf;
625
+ if ((error = tree_iterator_compute_path(path, entry)) < 0)
626
+ break;
356
627
 
357
- for (i = head->current, n_entries = 0; i < head->next; ++i) {
358
- git_tree *tree = head->entries[i]->tree;
359
- size_t j, max_j = git_tree_entrycount(tree);
628
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size,
629
+ frame->entries.length, tree->entries.size);
630
+ git_vector_size_hint(&frame->entries, new_size);
360
631
 
361
- for (j = 0; j < max_j; ++j) {
362
- tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
363
- GITERR_CHECK_ALLOC(entry);
632
+ git_array_foreach(tree->entries, i, tree_entry) {
633
+ new_entry = git_pool_malloc(&iter->entry_pool, 1);
634
+ GIT_ERROR_CHECK_ALLOC(new_entry);
364
635
 
365
- entry->parent = head->entries[i];
366
- entry->te = git_tree_entry_byindex(tree, j);
367
- entry->tree = NULL;
636
+ new_entry->tree_entry = tree_entry;
637
+ new_entry->parent_path = path->ptr;
368
638
 
369
- tf->entries[n_entries++] = entry;
639
+ if ((error = git_vector_insert(&frame->entries, new_entry)) < 0)
640
+ break;
370
641
  }
371
- }
372
642
 
373
- /* if ignore_case, sort entries case insensitively */
374
- if (iterator__ignore_case(ti))
375
- git__tsort_r(
376
- (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
643
+ if (error)
644
+ break;
377
645
 
378
- /* pick tf->current based on "start" (or start at zero) */
379
- if (head->startlen > 0) {
380
- git__bsearch_r((void **)tf->entries, tf->n_entries, head,
381
- tree_iterator__search_cmp, ti, &tf->current);
646
+ parent_frame->next_idx++;
647
+ }
382
648
 
383
- while (tf->current &&
384
- !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
385
- tf->current--;
649
+ return error;
650
+ }
386
651
 
387
- if ((tf->start = strchr(head->start, '/')) != NULL) {
388
- tf->start++;
389
- tf->startlen = strlen(tf->start);
390
- }
391
- }
652
+ GIT_INLINE(int) tree_iterator_frame_push(
653
+ tree_iterator *iter, tree_iterator_entry *entry)
654
+ {
655
+ tree_iterator_frame *parent_frame, *frame;
656
+ git_tree *tree = NULL;
657
+ int error;
392
658
 
393
- ti->path_has_filename = ti->entry_is_current = false;
659
+ if ((error = git_tree_lookup(&tree,
660
+ iter->base.repo, entry->tree_entry->oid)) < 0 ||
661
+ (error = tree_iterator_frame_init(iter, tree, entry)) < 0)
662
+ goto done;
394
663
 
395
- if ((error = tree_iterator__set_next(ti, tf)) < 0)
396
- return error;
664
+ parent_frame = tree_iterator_parent_frame(iter);
665
+ frame = tree_iterator_current_frame(iter);
397
666
 
398
- /* autoexpand as needed */
399
- if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
400
- return tree_iterator__push_frame(ti);
667
+ /* if we're case insensitive, then we may have another directory that
668
+ * is (case insensitively) equal to this one. coalesce those children
669
+ * into this tree.
670
+ */
671
+ if (iterator__ignore_case(&iter->base))
672
+ error = tree_iterator_frame_push_neighbors(iter,
673
+ parent_frame, frame, entry->tree_entry->filename);
401
674
 
402
- return 0;
675
+ done:
676
+ git_tree_free(tree);
677
+ return error;
403
678
  }
404
679
 
405
- static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
680
+ static void tree_iterator_frame_pop(tree_iterator *iter)
406
681
  {
407
- tree_iterator_frame *tf = ti->head;
682
+ tree_iterator_frame *frame;
683
+ git_buf *buf = NULL;
684
+ git_tree *tree;
685
+ size_t i;
408
686
 
409
- if (!tf->up)
410
- return false;
687
+ assert(iter->frames.size);
411
688
 
412
- ti->head = tf->up;
413
- ti->head->down = NULL;
689
+ frame = git_array_pop(iter->frames);
414
690
 
415
- tree_iterator__move_to_next(ti, tf);
691
+ git_vector_free(&frame->entries);
692
+ git_tree_free(frame->tree);
416
693
 
417
- if (!final) { /* if final, don't bother to clean up */
418
- git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
419
- git_buf_rtruncate_at_char(&ti->path, '/');
420
- }
694
+ do {
695
+ buf = git_array_pop(frame->similar_paths);
696
+ git_buf_dispose(buf);
697
+ } while (buf != NULL);
421
698
 
422
- git__free(tf);
699
+ git_array_clear(frame->similar_paths);
423
700
 
424
- return true;
701
+ git_vector_foreach(&frame->similar_trees, i, tree)
702
+ git_tree_free(tree);
703
+
704
+ git_vector_free(&frame->similar_trees);
705
+
706
+ git_buf_dispose(&frame->path);
425
707
  }
426
708
 
427
- static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
709
+ static int tree_iterator_current(
710
+ const git_index_entry **out, git_iterator *i)
428
711
  {
429
- while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
712
+ tree_iterator *iter = (tree_iterator *)i;
430
713
 
431
- if (!final) {
432
- ti->head->current = to_end ? ti->head->n_entries : 0;
433
- ti->path_ambiguities = 0;
434
- git_buf_clear(&ti->path);
714
+ if (!iterator__has_been_accessed(i))
715
+ return iter->base.cb->advance(out, i);
716
+
717
+ if (!iter->frames.size) {
718
+ *out = NULL;
719
+ return GIT_ITEROVER;
435
720
  }
721
+
722
+ *out = &iter->entry;
723
+ return 0;
436
724
  }
437
725
 
438
- static int tree_iterator__update_entry(tree_iterator *ti)
726
+ static void tree_iterator_set_current(
727
+ tree_iterator *iter,
728
+ tree_iterator_frame *frame,
729
+ tree_iterator_entry *entry)
439
730
  {
440
- tree_iterator_frame *tf;
441
- const git_tree_entry *te;
731
+ git_tree_entry *tree_entry = entry->tree_entry;
442
732
 
443
- if (ti->entry_is_current)
444
- return 0;
733
+ frame->current = entry;
445
734
 
446
- tf = ti->head;
447
- te = tf->entries[tf->current]->te;
735
+ memset(&iter->entry, 0x0, sizeof(git_index_entry));
736
+
737
+ iter->entry.mode = tree_entry->attr;
738
+ iter->entry.path = iter->entry_path.ptr;
739
+ git_oid_cpy(&iter->entry.id, tree_entry->oid);
740
+ }
448
741
 
449
- ti->entry.mode = te->attr;
450
- git_oid_cpy(&ti->entry.oid, &te->oid);
742
+ static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
743
+ {
744
+ tree_iterator *iter = (tree_iterator *)i;
745
+ int error = 0;
451
746
 
452
- ti->entry.path = tree_iterator__current_filename(ti, te);
453
- GITERR_CHECK_ALLOC(ti->entry.path);
747
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
454
748
 
455
- if (ti->path_ambiguities > 0)
456
- tree_iterator__rewrite_filename(ti);
749
+ /* examine tree entries until we find the next one to return */
750
+ while (true) {
751
+ tree_iterator_entry *prev_entry, *entry;
752
+ tree_iterator_frame *frame;
753
+ bool is_tree;
457
754
 
458
- if (iterator__past_end(ti, ti->entry.path)) {
459
- tree_iterator__pop_all(ti, true, false);
460
- return GIT_ITEROVER;
461
- }
755
+ if ((frame = tree_iterator_current_frame(iter)) == NULL) {
756
+ error = GIT_ITEROVER;
757
+ break;
758
+ }
462
759
 
463
- ti->entry_is_current = true;
760
+ /* no more entries in this frame. pop the frame out */
761
+ if (frame->next_idx == frame->entries.length) {
762
+ tree_iterator_frame_pop(iter);
763
+ continue;
764
+ }
464
765
 
465
- return 0;
466
- }
766
+ /* we may have coalesced the contents of case-insensitively same-named
767
+ * directories, so do the sort now.
768
+ */
769
+ if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries))
770
+ git_vector_sort(&frame->entries);
771
+
772
+ /* we have more entries in the current frame, that's our next entry */
773
+ prev_entry = tree_iterator_current_entry(frame);
774
+ entry = frame->entries.contents[frame->next_idx];
775
+ frame->next_idx++;
776
+
777
+ /* we can have collisions when iterating case insensitively. (eg,
778
+ * 'A/a' and 'a/A'). squash this one if it's already been seen.
779
+ */
780
+ if (iterator__ignore_case(&iter->base) &&
781
+ prev_entry &&
782
+ tree_iterator_entry_cmp_icase(prev_entry, entry) == 0)
783
+ continue;
784
+
785
+ if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0)
786
+ break;
467
787
 
468
- static int tree_iterator__current(
469
- const git_index_entry **entry, git_iterator *self)
470
- {
471
- int error;
472
- tree_iterator *ti = (tree_iterator *)self;
473
- tree_iterator_frame *tf = ti->head;
788
+ /* if this path is before our start, advance over this entry */
789
+ if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false))
790
+ continue;
474
791
 
475
- iterator__clear_entry(entry);
792
+ /* if this path is after our end, stop */
793
+ if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) {
794
+ error = GIT_ITEROVER;
795
+ break;
796
+ }
476
797
 
477
- if (tf->current >= tf->n_entries)
478
- return GIT_ITEROVER;
798
+ /* if we have a list of paths we're interested in, examine it */
799
+ if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr))
800
+ continue;
479
801
 
480
- if ((error = tree_iterator__update_entry(ti)) < 0)
481
- return error;
802
+ is_tree = git_tree_entry__is_tree(entry->tree_entry);
482
803
 
483
- if (entry)
484
- *entry = &ti->entry;
804
+ /* if we are *not* including trees then advance over this entry */
805
+ if (is_tree && !iterator__include_trees(iter)) {
485
806
 
486
- ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
807
+ /* if we've found a tree (and are not returning it to the caller)
808
+ * and we are autoexpanding, then we want to return the first
809
+ * child. push the new directory and advance.
810
+ */
811
+ if (iterator__do_autoexpand(iter)) {
812
+ if ((error = tree_iterator_frame_push(iter, entry)) < 0)
813
+ break;
814
+ }
487
815
 
488
- return 0;
489
- }
816
+ continue;
817
+ }
490
818
 
491
- static int tree_iterator__advance_into(
492
- const git_index_entry **entry, git_iterator *self)
493
- {
494
- int error = 0;
495
- tree_iterator *ti = (tree_iterator *)self;
819
+ tree_iterator_set_current(iter, frame, entry);
496
820
 
497
- iterator__clear_entry(entry);
821
+ /* if we are autoexpanding, then push this as a new frame, so that
822
+ * the next call to `advance` will dive into this directory.
823
+ */
824
+ if (is_tree && iterator__do_autoexpand(iter))
825
+ error = tree_iterator_frame_push(iter, entry);
498
826
 
499
- if (tree_iterator__at_tree(ti))
500
- error = tree_iterator__push_frame(ti);
827
+ break;
828
+ }
501
829
 
502
- if (!error && entry)
503
- error = tree_iterator__current(entry, self);
830
+ if (out)
831
+ *out = (error == 0) ? &iter->entry : NULL;
504
832
 
505
833
  return error;
506
834
  }
507
835
 
508
- static int tree_iterator__advance(
509
- const git_index_entry **entry, git_iterator *self)
836
+ static int tree_iterator_advance_into(
837
+ const git_index_entry **out, git_iterator *i)
510
838
  {
839
+ tree_iterator *iter = (tree_iterator *)i;
840
+ tree_iterator_frame *frame;
841
+ tree_iterator_entry *prev_entry;
511
842
  int error;
512
- tree_iterator *ti = (tree_iterator *)self;
513
- tree_iterator_frame *tf = ti->head;
514
843
 
515
- iterator__clear_entry(entry);
844
+ if (out)
845
+ *out = NULL;
516
846
 
517
- if (tf->current >= tf->n_entries)
847
+ if ((frame = tree_iterator_current_frame(iter)) == NULL)
518
848
  return GIT_ITEROVER;
519
849
 
520
- if (!iterator__has_been_accessed(ti))
521
- return tree_iterator__current(entry, self);
850
+ /* get the last seen entry */
851
+ prev_entry = tree_iterator_current_entry(frame);
522
852
 
523
- if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
524
- tree_iterator__at_tree(ti))
525
- return tree_iterator__advance_into(entry, self);
526
-
527
- if (ti->path_has_filename) {
528
- git_buf_rtruncate_at_char(&ti->path, '/');
529
- ti->path_has_filename = ti->entry_is_current = false;
530
- }
531
-
532
- /* scan forward and up, advancing in frame or popping frame when done */
533
- while (!tree_iterator__move_to_next(ti, tf) &&
534
- tree_iterator__pop_frame(ti, false))
535
- tf = ti->head;
853
+ /* it's legal to call advance_into when auto-expand is on. in this case,
854
+ * we will have pushed a new (empty) frame on to the stack for this
855
+ * new directory. since it's empty, its current_entry should be null.
856
+ */
857
+ assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
536
858
 
537
- /* find next and load trees */
538
- if ((error = tree_iterator__set_next(ti, tf)) < 0)
539
- return error;
859
+ if (prev_entry) {
860
+ if (!git_tree_entry__is_tree(prev_entry->tree_entry))
861
+ return 0;
540
862
 
541
- /* deal with include_trees / auto_expand as needed */
542
- if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
543
- return tree_iterator__advance_into(entry, self);
863
+ if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0)
864
+ return error;
865
+ }
544
866
 
545
- return tree_iterator__current(entry, self);
867
+ /* we've advanced into the directory in question, let advance
868
+ * find the first entry
869
+ */
870
+ return tree_iterator_advance(out, i);
546
871
  }
547
872
 
548
- static int tree_iterator__seek(git_iterator *self, const char *prefix)
873
+ static int tree_iterator_advance_over(
874
+ const git_index_entry **out,
875
+ git_iterator_status_t *status,
876
+ git_iterator *i)
549
877
  {
550
- GIT_UNUSED(self); GIT_UNUSED(prefix);
551
- return -1;
878
+ *status = GIT_ITERATOR_STATUS_NORMAL;
879
+ return git_iterator_advance(out, i);
552
880
  }
553
881
 
554
- static int tree_iterator__reset(
555
- git_iterator *self, const char *start, const char *end)
882
+ static void tree_iterator_clear(tree_iterator *iter)
556
883
  {
557
- tree_iterator *ti = (tree_iterator *)self;
884
+ while (iter->frames.size)
885
+ tree_iterator_frame_pop(iter);
558
886
 
559
- tree_iterator__pop_all(ti, false, false);
887
+ git_array_clear(iter->frames);
560
888
 
561
- if (iterator__reset_range(self, start, end) < 0)
562
- return -1;
889
+ git_pool_clear(&iter->entry_pool);
890
+ git_buf_clear(&iter->entry_path);
563
891
 
564
- return tree_iterator__push_frame(ti); /* re-expand root tree */
892
+ iterator_clear(&iter->base);
565
893
  }
566
894
 
567
- static int tree_iterator__at_end(git_iterator *self)
895
+ static int tree_iterator_init(tree_iterator *iter)
568
896
  {
569
- tree_iterator *ti = (tree_iterator *)self;
570
- return (ti->head->current >= ti->head->n_entries);
571
- }
897
+ int error;
572
898
 
573
- static void tree_iterator__free(git_iterator *self)
574
- {
575
- tree_iterator *ti = (tree_iterator *)self;
899
+ git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry));
900
+
901
+ if ((error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0)
902
+ return error;
576
903
 
577
- tree_iterator__pop_all(ti, true, false);
904
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
578
905
 
579
- git_tree_free(ti->head->entries[0]->tree);
580
- git__free(ti->head);
581
- git_pool_clear(&ti->pool);
582
- git_buf_free(&ti->path);
906
+ return 0;
583
907
  }
584
908
 
585
- static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
909
+ static int tree_iterator_reset(git_iterator *i)
586
910
  {
587
- size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
588
- tree_iterator_frame *root = git__calloc(sz, sizeof(char));
589
- GITERR_CHECK_ALLOC(root);
911
+ tree_iterator *iter = (tree_iterator *)i;
590
912
 
591
- root->n_entries = 1;
592
- root->next = 1;
593
- root->start = ti->base.start;
594
- root->startlen = root->start ? strlen(root->start) : 0;
595
- root->entries[0] = git_pool_mallocz(&ti->pool, 1);
596
- GITERR_CHECK_ALLOC(root->entries[0]);
597
- root->entries[0]->tree = tree;
913
+ tree_iterator_clear(iter);
914
+ return tree_iterator_init(iter);
915
+ }
916
+
917
+ static void tree_iterator_free(git_iterator *i)
918
+ {
919
+ tree_iterator *iter = (tree_iterator *)i;
598
920
 
599
- ti->head = ti->root = root;
921
+ tree_iterator_clear(iter);
600
922
 
601
- return 0;
923
+ git_tree_free(iter->root);
924
+ git_buf_dispose(&iter->entry_path);
602
925
  }
603
926
 
604
927
  int git_iterator_for_tree(
605
- git_iterator **iter,
928
+ git_iterator **out,
606
929
  git_tree *tree,
607
- git_iterator_flag_t flags,
608
- const char *start,
609
- const char *end)
930
+ git_iterator_options *options)
610
931
  {
932
+ tree_iterator *iter;
611
933
  int error;
612
- tree_iterator *ti;
613
934
 
614
- if (tree == NULL)
615
- return git_iterator_for_nothing(iter, flags, start, end);
935
+ static git_iterator_callbacks callbacks = {
936
+ tree_iterator_current,
937
+ tree_iterator_advance,
938
+ tree_iterator_advance_into,
939
+ tree_iterator_advance_over,
940
+ tree_iterator_reset,
941
+ tree_iterator_free
942
+ };
616
943
 
617
- if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
618
- return error;
944
+ *out = NULL;
619
945
 
620
- ti = git__calloc(1, sizeof(tree_iterator));
621
- GITERR_CHECK_ALLOC(ti);
946
+ if (tree == NULL)
947
+ return git_iterator_for_nothing(out, options);
622
948
 
623
- ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
949
+ iter = git__calloc(1, sizeof(tree_iterator));
950
+ GIT_ERROR_CHECK_ALLOC(iter);
624
951
 
625
- if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
626
- goto fail;
627
- ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
952
+ iter->base.type = GIT_ITERATOR_TYPE_TREE;
953
+ iter->base.cb = &callbacks;
628
954
 
629
- if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
630
- (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
631
- (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
632
- goto fail;
955
+ if ((error = iterator_init_common(&iter->base,
956
+ git_tree_owner(tree), NULL, options)) < 0 ||
957
+ (error = git_tree_dup(&iter->root, tree)) < 0 ||
958
+ (error = tree_iterator_init(iter)) < 0)
959
+ goto on_error;
633
960
 
634
- *iter = (git_iterator *)ti;
961
+ *out = &iter->base;
635
962
  return 0;
636
963
 
637
- fail:
638
- git_iterator_free((git_iterator *)ti);
964
+ on_error:
965
+ git_iterator_free(&iter->base);
639
966
  return error;
640
967
  }
641
968
 
642
-
643
- typedef struct {
644
- git_iterator base;
645
- git_iterator_callbacks cb;
646
- git_index *index;
647
- size_t current;
648
- /* when not in autoexpand mode, use these to represent "tree" state */
649
- git_buf partial;
650
- size_t partial_pos;
651
- char restore_terminator;
652
- git_index_entry tree_entry;
653
- } index_iterator;
654
-
655
- static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
969
+ int git_iterator_current_tree_entry(
970
+ const git_tree_entry **tree_entry, git_iterator *i)
656
971
  {
657
- const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
658
-
659
- if (ie != NULL && iterator__past_end(ii, ie->path)) {
660
- ii->current = git_index_entrycount(ii->index);
661
- ie = NULL;
662
- }
972
+ tree_iterator *iter;
973
+ tree_iterator_frame *frame;
974
+ tree_iterator_entry *entry;
663
975
 
664
- return ie;
665
- }
976
+ assert(i->type == GIT_ITERATOR_TYPE_TREE);
666
977
 
667
- static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
668
- {
669
- const git_index_entry *ie;
978
+ iter = (tree_iterator *)i;
670
979
 
671
- while ((ie = index_iterator__index_entry(ii)) != NULL &&
672
- git_index_entry_stage(ie) != 0)
673
- ii->current++;
980
+ frame = tree_iterator_current_frame(iter);
981
+ entry = tree_iterator_current_entry(frame);
674
982
 
675
- return ie;
983
+ *tree_entry = entry->tree_entry;
984
+ return 0;
676
985
  }
677
986
 
678
- static void index_iterator__next_prefix_tree(index_iterator *ii)
987
+ int git_iterator_current_parent_tree(
988
+ const git_tree **parent_tree, git_iterator *i, size_t depth)
679
989
  {
680
- const char *slash;
990
+ tree_iterator *iter;
991
+ tree_iterator_frame *frame;
681
992
 
682
- if (!iterator__include_trees(ii))
683
- return;
993
+ assert(i->type == GIT_ITERATOR_TYPE_TREE);
684
994
 
685
- slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
995
+ iter = (tree_iterator *)i;
686
996
 
687
- if (slash != NULL) {
688
- ii->partial_pos = (slash - ii->partial.ptr) + 1;
689
- ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
690
- ii->partial.ptr[ii->partial_pos] = '\0';
691
- } else {
692
- ii->partial_pos = ii->partial.size;
693
- }
997
+ assert(depth < iter->frames.size);
998
+ frame = &iter->frames.ptr[iter->frames.size-depth-1];
694
999
 
695
- if (index_iterator__index_entry(ii) == NULL)
696
- ii->partial_pos = ii->partial.size;
1000
+ *parent_tree = frame->tree;
1001
+ return 0;
697
1002
  }
698
1003
 
699
- static int index_iterator__first_prefix_tree(index_iterator *ii)
700
- {
701
- const git_index_entry *ie = index_iterator__skip_conflicts(ii);
702
- const char *scan, *prior, *slash;
1004
+ /* Filesystem iterator */
703
1005
 
704
- if (!ie || !iterator__include_trees(ii))
705
- return 0;
1006
+ typedef struct {
1007
+ struct stat st;
1008
+ size_t path_len;
1009
+ iterator_pathlist_search_t match;
1010
+ git_oid id;
1011
+ char path[GIT_FLEX_ARRAY];
1012
+ } filesystem_iterator_entry;
706
1013
 
707
- /* find longest common prefix with prior index entry */
708
- for (scan = slash = ie->path, prior = ii->partial.ptr;
709
- *scan && *scan == *prior; ++scan, ++prior)
710
- if (*scan == '/')
711
- slash = scan;
1014
+ typedef struct {
1015
+ git_vector entries;
1016
+ git_pool entry_pool;
1017
+ size_t next_idx;
712
1018
 
713
- if (git_buf_sets(&ii->partial, ie->path) < 0)
714
- return -1;
1019
+ size_t path_len;
1020
+ int is_ignored;
1021
+ } filesystem_iterator_frame;
1022
+
1023
+ typedef struct {
1024
+ git_iterator base;
1025
+ char *root;
1026
+ size_t root_len;
715
1027
 
716
- ii->partial_pos = (slash - ie->path) + 1;
717
- index_iterator__next_prefix_tree(ii);
1028
+ unsigned int dirload_flags;
718
1029
 
719
- return 0;
720
- }
1030
+ git_tree *tree;
1031
+ git_index *index;
1032
+ git_vector index_snapshot;
721
1033
 
722
- #define index_iterator__at_tree(I) \
723
- (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
1034
+ git_array_t(filesystem_iterator_frame) frames;
1035
+ git_ignores ignores;
1036
+
1037
+ /* info about the current entry */
1038
+ git_index_entry entry;
1039
+ git_buf current_path;
1040
+ int current_is_ignored;
1041
+
1042
+ /* temporary buffer for advance_over */
1043
+ git_buf tmp_buf;
1044
+ } filesystem_iterator;
724
1045
 
725
- static int index_iterator__current(
726
- const git_index_entry **entry, git_iterator *self)
1046
+
1047
+ GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame(
1048
+ filesystem_iterator *iter)
727
1049
  {
728
- index_iterator *ii = (index_iterator *)self;
729
- const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
1050
+ return iter->frames.size > 1 ?
1051
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
1052
+ }
730
1053
 
731
- if (ie != NULL && index_iterator__at_tree(ii)) {
732
- ii->tree_entry.path = ii->partial.ptr;
733
- ie = &ii->tree_entry;
734
- }
1054
+ GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame(
1055
+ filesystem_iterator *iter)
1056
+ {
1057
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
1058
+ }
735
1059
 
736
- if (entry)
737
- *entry = ie;
1060
+ GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry(
1061
+ filesystem_iterator_frame *frame)
1062
+ {
1063
+ return frame->next_idx == 0 ?
1064
+ NULL : frame->entries.contents[frame->next_idx-1];
1065
+ }
738
1066
 
739
- ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1067
+ static int filesystem_iterator_entry_cmp(const void *_a, const void *_b)
1068
+ {
1069
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1070
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
740
1071
 
741
- return (ie != NULL) ? 0 : GIT_ITEROVER;
1072
+ return git__strcmp(a->path, b->path);
742
1073
  }
743
1074
 
744
- static int index_iterator__at_end(git_iterator *self)
1075
+ static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b)
745
1076
  {
746
- index_iterator *ii = (index_iterator *)self;
747
- return (ii->current >= git_index_entrycount(ii->index));
1077
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1078
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
1079
+
1080
+ return git__strcasecmp(a->path, b->path);
748
1081
  }
749
1082
 
750
- static int index_iterator__advance(
751
- const git_index_entry **entry, git_iterator *self)
1083
+ #define FILESYSTEM_MAX_DEPTH 100
1084
+
1085
+ /**
1086
+ * Figure out if an entry is a submodule.
1087
+ *
1088
+ * We consider it a submodule if the path is listed as a submodule in
1089
+ * either the tree or the index.
1090
+ */
1091
+ static int filesystem_iterator_is_submodule(
1092
+ bool *out, filesystem_iterator *iter, const char *path, size_t path_len)
752
1093
  {
753
- index_iterator *ii = (index_iterator *)self;
754
- size_t entrycount = git_index_entrycount(ii->index);
755
- const git_index_entry *ie;
1094
+ bool is_submodule = false;
1095
+ int error;
756
1096
 
757
- if (!iterator__has_been_accessed(ii))
758
- return index_iterator__current(entry, self);
1097
+ *out = false;
759
1098
 
760
- if (index_iterator__at_tree(ii)) {
761
- if (iterator__do_autoexpand(ii)) {
762
- ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
763
- index_iterator__next_prefix_tree(ii);
764
- } else {
765
- /* advance to sibling tree (i.e. find entry with new prefix) */
766
- while (ii->current < entrycount) {
767
- ii->current++;
1099
+ /* first see if this path is a submodule in HEAD */
1100
+ if (iter->tree) {
1101
+ git_tree_entry *entry;
768
1102
 
769
- if (!(ie = git_index_get_byindex(ii->index, ii->current)) ||
770
- ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
771
- break;
772
- }
1103
+ error = git_tree_entry_bypath(&entry, iter->tree, path);
773
1104
 
774
- if (index_iterator__first_prefix_tree(ii) < 0)
775
- return -1;
776
- }
777
- } else {
778
- if (ii->current < entrycount)
779
- ii->current++;
1105
+ if (error < 0 && error != GIT_ENOTFOUND)
1106
+ return error;
780
1107
 
781
- if (index_iterator__first_prefix_tree(ii) < 0)
782
- return -1;
1108
+ if (!error) {
1109
+ is_submodule = (entry->attr == GIT_FILEMODE_COMMIT);
1110
+ git_tree_entry_free(entry);
1111
+ }
783
1112
  }
784
1113
 
785
- return index_iterator__current(entry, self);
786
- }
1114
+ if (!is_submodule && iter->base.index) {
1115
+ size_t pos;
787
1116
 
788
- static int index_iterator__advance_into(
789
- const git_index_entry **entry, git_iterator *self)
790
- {
791
- index_iterator *ii = (index_iterator *)self;
792
- const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
1117
+ error = git_index_snapshot_find(&pos,
1118
+ &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0);
793
1119
 
794
- if (ie != NULL && index_iterator__at_tree(ii)) {
795
- if (ii->restore_terminator)
796
- ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
797
- index_iterator__next_prefix_tree(ii);
798
- }
1120
+ if (error < 0 && error != GIT_ENOTFOUND)
1121
+ return error;
799
1122
 
800
- return index_iterator__current(entry, self);
801
- }
1123
+ if (!error) {
1124
+ git_index_entry *e = git_vector_get(&iter->index_snapshot, pos);
1125
+ is_submodule = (e->mode == GIT_FILEMODE_COMMIT);
1126
+ }
1127
+ }
802
1128
 
803
- static int index_iterator__seek(git_iterator *self, const char *prefix)
804
- {
805
- GIT_UNUSED(self); GIT_UNUSED(prefix);
806
- return -1;
1129
+ *out = is_submodule;
1130
+ return 0;
807
1131
  }
808
1132
 
809
- static int index_iterator__reset(
810
- git_iterator *self, const char *start, const char *end)
1133
+ static void filesystem_iterator_frame_push_ignores(
1134
+ filesystem_iterator *iter,
1135
+ filesystem_iterator_entry *frame_entry,
1136
+ filesystem_iterator_frame *new_frame)
811
1137
  {
812
- index_iterator *ii = (index_iterator *)self;
813
- const git_index_entry *ie;
1138
+ filesystem_iterator_frame *previous_frame;
1139
+ const char *path = frame_entry ? frame_entry->path : "";
814
1140
 
815
- if (iterator__reset_range(self, start, end) < 0)
816
- return -1;
1141
+ if (!iterator__honor_ignores(&iter->base))
1142
+ return;
817
1143
 
818
- ii->current = ii->base.start ?
819
- git_index__prefix_position(ii->index, ii->base.start) : 0;
1144
+ if (git_ignore__lookup(&new_frame->is_ignored,
1145
+ &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) {
1146
+ git_error_clear();
1147
+ new_frame->is_ignored = GIT_IGNORE_NOTFOUND;
1148
+ }
820
1149
 
821
- if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
822
- return 0;
1150
+ /* if this is not the top level directory... */
1151
+ if (frame_entry) {
1152
+ const char *relative_path;
823
1153
 
824
- if (git_buf_sets(&ii->partial, ie->path) < 0)
825
- return -1;
1154
+ previous_frame = filesystem_iterator_parent_frame(iter);
826
1155
 
827
- ii->partial_pos = 0;
1156
+ /* push new ignores for files in this directory */
1157
+ relative_path = frame_entry->path + previous_frame->path_len;
828
1158
 
829
- if (ii->base.start) {
830
- size_t startlen = strlen(ii->base.start);
1159
+ /* inherit ignored from parent if no rule specified */
1160
+ if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND)
1161
+ new_frame->is_ignored = previous_frame->is_ignored;
831
1162
 
832
- ii->partial_pos = (startlen > ii->partial.size) ?
833
- ii->partial.size : startlen;
1163
+ git_ignore__push_dir(&iter->ignores, relative_path);
834
1164
  }
835
-
836
- index_iterator__next_prefix_tree(ii);
837
-
838
- return 0;
839
1165
  }
840
1166
 
841
- static void index_iterator__free(git_iterator *self)
1167
+ static void filesystem_iterator_frame_pop_ignores(
1168
+ filesystem_iterator *iter)
842
1169
  {
843
- index_iterator *ii = (index_iterator *)self;
844
- git_index_free(ii->index);
845
- ii->index = NULL;
846
-
847
- git_buf_free(&ii->partial);
1170
+ if (iterator__honor_ignores(&iter->base))
1171
+ git_ignore__pop_dir(&iter->ignores);
848
1172
  }
849
1173
 
850
- int git_iterator_for_index(
851
- git_iterator **iter,
852
- git_index *index,
853
- git_iterator_flag_t flags,
854
- const char *start,
855
- const char *end)
1174
+ GIT_INLINE(bool) filesystem_iterator_examine_path(
1175
+ bool *is_dir_out,
1176
+ iterator_pathlist_search_t *match_out,
1177
+ filesystem_iterator *iter,
1178
+ filesystem_iterator_entry *frame_entry,
1179
+ const char *path,
1180
+ size_t path_len)
856
1181
  {
857
- index_iterator *ii = git__calloc(1, sizeof(index_iterator));
858
- GITERR_CHECK_ALLOC(ii);
1182
+ bool is_dir = 0;
1183
+ iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL;
1184
+
1185
+ *is_dir_out = false;
1186
+ *match_out = ITERATOR_PATHLIST_NONE;
1187
+
1188
+ if (iter->base.start_len) {
1189
+ int cmp = iter->base.strncomp(path, iter->base.start, path_len);
1190
+
1191
+ /* we haven't stat'ed `path` yet, so we don't yet know if it's a
1192
+ * directory or not. special case if the current path may be a
1193
+ * directory that matches the start prefix.
1194
+ */
1195
+ if (cmp == 0) {
1196
+ if (iter->base.start[path_len] == '/')
1197
+ is_dir = true;
859
1198
 
860
- ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
1199
+ else if (iter->base.start[path_len] != '\0')
1200
+ cmp = -1;
1201
+ }
861
1202
 
862
- if (index->ignore_case) {
863
- ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
864
- ii->base.prefixcomp = git__prefixcmp_icase;
1203
+ if (cmp < 0)
1204
+ return false;
865
1205
  }
866
1206
 
867
- ii->index = index;
868
- GIT_REFCOUNT_INC(index);
1207
+ if (iter->base.end_len) {
1208
+ int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len);
869
1209
 
870
- git_buf_init(&ii->partial, 0);
871
- ii->tree_entry.mode = GIT_FILEMODE_TREE;
1210
+ if (cmp > 0)
1211
+ return false;
1212
+ }
872
1213
 
873
- index_iterator__reset((git_iterator *)ii, NULL, NULL);
1214
+ /* if we have a pathlist that we're limiting to, examine this path now
1215
+ * to avoid a `stat` if we're not interested in the path.
1216
+ */
1217
+ if (iter->base.pathlist.length) {
1218
+ /* if our parent was explicitly included, so too are we */
1219
+ if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT)
1220
+ match = ITERATOR_PATHLIST_FULL;
1221
+ else
1222
+ match = iterator_pathlist_search(&iter->base, path, path_len);
874
1223
 
875
- *iter = (git_iterator *)ii;
1224
+ if (match == ITERATOR_PATHLIST_NONE)
1225
+ return false;
876
1226
 
877
- return 0;
1227
+ /* Ensure that the pathlist entry lines up with what we expected */
1228
+ if (match == ITERATOR_PATHLIST_IS_DIR ||
1229
+ match == ITERATOR_PATHLIST_IS_PARENT)
1230
+ is_dir = true;
1231
+ }
1232
+
1233
+ *is_dir_out = is_dir;
1234
+ *match_out = match;
1235
+ return true;
878
1236
  }
879
1237
 
1238
+ GIT_INLINE(bool) filesystem_iterator_is_dot_git(
1239
+ filesystem_iterator *iter, const char *path, size_t path_len)
1240
+ {
1241
+ size_t len;
880
1242
 
881
- typedef struct fs_iterator_frame fs_iterator_frame;
882
- struct fs_iterator_frame {
883
- fs_iterator_frame *next;
884
- git_vector entries;
885
- size_t index;
886
- };
1243
+ if (!iterator__ignore_dot_git(&iter->base))
1244
+ return false;
887
1245
 
888
- typedef struct fs_iterator fs_iterator;
889
- struct fs_iterator {
890
- git_iterator base;
891
- git_iterator_callbacks cb;
892
- fs_iterator_frame *stack;
893
- git_index_entry entry;
894
- git_buf path;
895
- size_t root_len;
896
- int depth;
1246
+ if ((len = path_len) < 4)
1247
+ return false;
897
1248
 
898
- int (*enter_dir_cb)(fs_iterator *self);
899
- int (*leave_dir_cb)(fs_iterator *self);
900
- int (*update_entry_cb)(fs_iterator *self);
901
- };
1249
+ if (path[len - 1] == '/')
1250
+ len--;
902
1251
 
903
- #define FS_MAX_DEPTH 100
1252
+ if (git__tolower(path[len - 1]) != 't' ||
1253
+ git__tolower(path[len - 2]) != 'i' ||
1254
+ git__tolower(path[len - 3]) != 'g' ||
1255
+ git__tolower(path[len - 4]) != '.')
1256
+ return false;
904
1257
 
905
- static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
1258
+ return (len == 4 || path[len - 5] == '/');
1259
+ }
1260
+
1261
+ static int filesystem_iterator_entry_hash(
1262
+ filesystem_iterator *iter,
1263
+ filesystem_iterator_entry *entry)
906
1264
  {
907
- fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
908
- git_vector_cmp entry_compare = CASESELECT(
909
- iterator__ignore_case(fi),
910
- git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
1265
+ git_buf fullpath = GIT_BUF_INIT;
1266
+ int error;
911
1267
 
912
- if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
913
- git__free(ff);
914
- ff = NULL;
1268
+ if (S_ISDIR(entry->st.st_mode)) {
1269
+ memset(&entry->id, 0, GIT_OID_RAWSZ);
1270
+ return 0;
915
1271
  }
916
1272
 
917
- return ff;
918
- }
1273
+ if (iter->base.type == GIT_ITERATOR_TYPE_WORKDIR)
1274
+ return git_repository_hashfile(&entry->id,
1275
+ iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL);
919
1276
 
920
- static void fs_iterator__free_frame(fs_iterator_frame *ff)
921
- {
922
- size_t i;
923
- git_path_with_stat *path;
1277
+ if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path)))
1278
+ error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB);
924
1279
 
925
- git_vector_foreach(&ff->entries, i, path)
926
- git__free(path);
927
- git_vector_free(&ff->entries);
928
- git__free(ff);
1280
+ git_buf_dispose(&fullpath);
1281
+ return error;
929
1282
  }
930
1283
 
931
- static void fs_iterator__pop_frame(
932
- fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
1284
+ static int filesystem_iterator_entry_init(
1285
+ filesystem_iterator_entry **out,
1286
+ filesystem_iterator *iter,
1287
+ filesystem_iterator_frame *frame,
1288
+ const char *path,
1289
+ size_t path_len,
1290
+ struct stat *statbuf,
1291
+ iterator_pathlist_search_t pathlist_match)
933
1292
  {
934
- if (fi && fi->stack == ff) {
935
- if (!ff->next && !pop_last) {
936
- memset(&fi->entry, 0, sizeof(fi->entry));
937
- return;
938
- }
1293
+ filesystem_iterator_entry *entry;
1294
+ size_t entry_size;
1295
+ int error = 0;
939
1296
 
940
- if (fi->leave_dir_cb)
941
- (void)fi->leave_dir_cb(fi);
1297
+ *out = NULL;
942
1298
 
943
- fi->stack = ff->next;
944
- fi->depth--;
945
- }
1299
+ /* Make sure to append two bytes, one for the path's null
1300
+ * termination, one for a possible trailing '/' for folders.
1301
+ */
1302
+ GIT_ERROR_CHECK_ALLOC_ADD(&entry_size,
1303
+ sizeof(filesystem_iterator_entry), path_len);
1304
+ GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, entry_size, 2);
946
1305
 
947
- fs_iterator__free_frame(ff);
948
- }
1306
+ entry = git_pool_malloc(&frame->entry_pool, entry_size);
1307
+ GIT_ERROR_CHECK_ALLOC(entry);
949
1308
 
950
- static int fs_iterator__update_entry(fs_iterator *fi);
951
- static int fs_iterator__advance_over(
952
- const git_index_entry **entry, git_iterator *self);
1309
+ entry->path_len = path_len;
1310
+ entry->match = pathlist_match;
1311
+ memcpy(entry->path, path, path_len);
1312
+ memcpy(&entry->st, statbuf, sizeof(struct stat));
953
1313
 
954
- static int fs_iterator__entry_cmp(const void *i, const void *item)
955
- {
956
- const fs_iterator *fi = (const fs_iterator *)i;
957
- const git_path_with_stat *ps = item;
958
- return fi->base.prefixcomp(fi->base.start, ps->path);
959
- }
1314
+ /* Suffix directory paths with a '/' */
1315
+ if (S_ISDIR(entry->st.st_mode))
1316
+ entry->path[entry->path_len++] = '/';
960
1317
 
961
- static void fs_iterator__seek_frame_start(
962
- fs_iterator *fi, fs_iterator_frame *ff)
963
- {
964
- if (!ff)
965
- return;
1318
+ entry->path[entry->path_len] = '\0';
966
1319
 
967
- if (fi->base.start)
968
- git_vector_bsearch2(
969
- &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
970
- else
971
- ff->index = 0;
1320
+ if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
1321
+ error = filesystem_iterator_entry_hash(iter, entry);
1322
+
1323
+ if (!error)
1324
+ *out = entry;
1325
+
1326
+ return error;
972
1327
  }
973
1328
 
974
- static int fs_iterator__expand_dir(fs_iterator *fi)
1329
+ static int filesystem_iterator_frame_push(
1330
+ filesystem_iterator *iter,
1331
+ filesystem_iterator_entry *frame_entry)
975
1332
  {
1333
+ filesystem_iterator_frame *new_frame = NULL;
1334
+ git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
1335
+ git_buf root = GIT_BUF_INIT;
1336
+ const char *path;
1337
+ filesystem_iterator_entry *entry;
1338
+ struct stat statbuf;
1339
+ size_t path_len;
976
1340
  int error;
977
- fs_iterator_frame *ff;
978
1341
 
979
- if (fi->depth > FS_MAX_DEPTH) {
980
- giterr_set(GITERR_REPOSITORY,
981
- "Directory nesting is too deep (%d)", fi->depth);
1342
+ if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
1343
+ git_error_set(GIT_ERROR_REPOSITORY,
1344
+ "directory nesting too deep (%"PRIuZ")", iter->frames.size);
982
1345
  return -1;
983
1346
  }
984
1347
 
985
- ff = fs_iterator__alloc_frame(fi);
986
- GITERR_CHECK_ALLOC(ff);
1348
+ new_frame = git_array_alloc(iter->frames);
1349
+ GIT_ERROR_CHECK_ALLOC(new_frame);
987
1350
 
988
- error = git_path_dirload_with_stat(
989
- fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
990
- fi->base.start, fi->base.end, &ff->entries);
1351
+ memset(new_frame, 0, sizeof(filesystem_iterator_frame));
991
1352
 
992
- if (error < 0) {
993
- fs_iterator__free_frame(ff);
994
- fs_iterator__advance_over(NULL, (git_iterator *)fi);
995
- return error;
1353
+ if (frame_entry)
1354
+ git_buf_joinpath(&root, iter->root, frame_entry->path);
1355
+ else
1356
+ git_buf_puts(&root, iter->root);
1357
+
1358
+ if (git_buf_oom(&root)) {
1359
+ error = -1;
1360
+ goto done;
996
1361
  }
997
1362
 
998
- if (ff->entries.length == 0) {
999
- fs_iterator__free_frame(ff);
1000
- return GIT_ENOTFOUND;
1363
+ new_frame->path_len = frame_entry ? frame_entry->path_len : 0;
1364
+
1365
+ /* Any error here is equivalent to the dir not existing, skip over it */
1366
+ if ((error = git_path_diriter_init(
1367
+ &diriter, root.ptr, iter->dirload_flags)) < 0) {
1368
+ error = GIT_ENOTFOUND;
1369
+ goto done;
1001
1370
  }
1002
1371
 
1003
- fs_iterator__seek_frame_start(fi, ff);
1372
+ if ((error = git_vector_init(&new_frame->entries, 64,
1373
+ iterator__ignore_case(&iter->base) ?
1374
+ filesystem_iterator_entry_cmp_icase :
1375
+ filesystem_iterator_entry_cmp)) < 0)
1376
+ goto done;
1004
1377
 
1005
- ff->next = fi->stack;
1006
- fi->stack = ff;
1007
- fi->depth++;
1378
+ git_pool_init(&new_frame->entry_pool, 1);
1008
1379
 
1009
- if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1010
- return error;
1380
+ /* check if this directory is ignored */
1381
+ filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame);
1382
+
1383
+ while ((error = git_path_diriter_next(&diriter)) == 0) {
1384
+ iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
1385
+ bool dir_expected = false;
1011
1386
 
1012
- return fs_iterator__update_entry(fi);
1387
+ if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
1388
+ goto done;
1389
+
1390
+ assert(path_len > iter->root_len);
1391
+
1392
+ /* remove the prefix if requested */
1393
+ path += iter->root_len;
1394
+ path_len -= iter->root_len;
1395
+
1396
+ /* examine start / end and the pathlist to see if this path is in it.
1397
+ * note that since we haven't yet stat'ed the path, we cannot know
1398
+ * whether it's a directory yet or not, so this can give us an
1399
+ * expected type (S_IFDIR or S_IFREG) that we should examine)
1400
+ */
1401
+ if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match,
1402
+ iter, frame_entry, path, path_len))
1403
+ continue;
1404
+
1405
+ /* TODO: don't need to stat if assume unchanged for this path and
1406
+ * we have an index, we can just copy the data out of it.
1407
+ */
1408
+
1409
+ if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) {
1410
+ /* file was removed between readdir and lstat */
1411
+ if (error == GIT_ENOTFOUND)
1412
+ continue;
1413
+
1414
+ /* treat the file as unreadable */
1415
+ memset(&statbuf, 0, sizeof(statbuf));
1416
+ statbuf.st_mode = GIT_FILEMODE_UNREADABLE;
1417
+
1418
+ error = 0;
1419
+ }
1420
+
1421
+ iter->base.stat_calls++;
1422
+
1423
+ /* Ignore wacky things in the filesystem */
1424
+ if (!S_ISDIR(statbuf.st_mode) &&
1425
+ !S_ISREG(statbuf.st_mode) &&
1426
+ !S_ISLNK(statbuf.st_mode) &&
1427
+ statbuf.st_mode != GIT_FILEMODE_UNREADABLE)
1428
+ continue;
1429
+
1430
+ if (filesystem_iterator_is_dot_git(iter, path, path_len))
1431
+ continue;
1432
+
1433
+ /* convert submodules to GITLINK and remove trailing slashes */
1434
+ if (S_ISDIR(statbuf.st_mode)) {
1435
+ bool submodule = false;
1436
+
1437
+ if ((error = filesystem_iterator_is_submodule(&submodule,
1438
+ iter, path, path_len)) < 0)
1439
+ goto done;
1440
+
1441
+ if (submodule)
1442
+ statbuf.st_mode = GIT_FILEMODE_COMMIT;
1443
+ }
1444
+
1445
+ /* Ensure that the pathlist entry lines up with what we expected */
1446
+ else if (dir_expected)
1447
+ continue;
1448
+
1449
+ if ((error = filesystem_iterator_entry_init(&entry,
1450
+ iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0)
1451
+ goto done;
1452
+
1453
+ git_vector_insert(&new_frame->entries, entry);
1454
+ }
1455
+
1456
+ if (error == GIT_ITEROVER)
1457
+ error = 0;
1458
+
1459
+ /* sort now that directory suffix is added */
1460
+ git_vector_sort(&new_frame->entries);
1461
+
1462
+ done:
1463
+ if (error < 0)
1464
+ git_array_pop(iter->frames);
1465
+
1466
+ git_buf_dispose(&root);
1467
+ git_path_diriter_free(&diriter);
1468
+ return error;
1013
1469
  }
1014
1470
 
1015
- static int fs_iterator__current(
1016
- const git_index_entry **entry, git_iterator *self)
1471
+ GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter)
1017
1472
  {
1018
- fs_iterator *fi = (fs_iterator *)self;
1019
- const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1473
+ filesystem_iterator_frame *frame;
1020
1474
 
1021
- if (entry)
1022
- *entry = fe;
1475
+ assert(iter->frames.size);
1023
1476
 
1024
- fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1477
+ frame = git_array_pop(iter->frames);
1478
+ filesystem_iterator_frame_pop_ignores(iter);
1025
1479
 
1026
- return (fe != NULL) ? 0 : GIT_ITEROVER;
1480
+ git_pool_clear(&frame->entry_pool);
1481
+ git_vector_free(&frame->entries);
1027
1482
  }
1028
1483
 
1029
- static int fs_iterator__at_end(git_iterator *self)
1484
+ static void filesystem_iterator_set_current(
1485
+ filesystem_iterator *iter,
1486
+ filesystem_iterator_entry *entry)
1030
1487
  {
1031
- return (((fs_iterator *)self)->entry.path == NULL);
1488
+ /*
1489
+ * Index entries are limited to 32 bit timestamps. We can safely
1490
+ * cast this since workdir times are only used in the cache; any
1491
+ * mismatch will cause a hash recomputation which is unfortunate
1492
+ * but affects only people who set their filetimes to 2038.
1493
+ * (Same with the file size.)
1494
+ */
1495
+ iter->entry.ctime.seconds = (int32_t)entry->st.st_ctime;
1496
+ iter->entry.mtime.seconds = (int32_t)entry->st.st_mtime;
1497
+
1498
+ #if defined(GIT_USE_NSEC)
1499
+ iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
1500
+ iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
1501
+ #else
1502
+ iter->entry.ctime.nanoseconds = 0;
1503
+ iter->entry.mtime.nanoseconds = 0;
1504
+ #endif
1505
+
1506
+ iter->entry.dev = entry->st.st_dev;
1507
+ iter->entry.ino = entry->st.st_ino;
1508
+ iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode);
1509
+ iter->entry.uid = entry->st.st_uid;
1510
+ iter->entry.gid = entry->st.st_gid;
1511
+ iter->entry.file_size = (uint32_t)entry->st.st_size;
1512
+
1513
+ if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
1514
+ git_oid_cpy(&iter->entry.id, &entry->id);
1515
+
1516
+ iter->entry.path = entry->path;
1517
+
1518
+ iter->current_is_ignored = GIT_IGNORE_UNCHECKED;
1032
1519
  }
1033
1520
 
1034
- static int fs_iterator__advance_into(
1035
- const git_index_entry **entry, git_iterator *iter)
1521
+ static int filesystem_iterator_current(
1522
+ const git_index_entry **out, git_iterator *i)
1036
1523
  {
1524
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1525
+
1526
+ if (!iterator__has_been_accessed(i))
1527
+ return iter->base.cb->advance(out, i);
1528
+
1529
+ if (!iter->frames.size) {
1530
+ *out = NULL;
1531
+ return GIT_ITEROVER;
1532
+ }
1533
+
1534
+ *out = &iter->entry;
1535
+ return 0;
1536
+ }
1537
+
1538
+ static int filesystem_iterator_is_dir(
1539
+ bool *is_dir,
1540
+ const filesystem_iterator *iter,
1541
+ const filesystem_iterator_entry *entry)
1542
+ {
1543
+ struct stat st;
1544
+ git_buf fullpath = GIT_BUF_INIT;
1037
1545
  int error = 0;
1038
- fs_iterator *fi = (fs_iterator *)iter;
1039
1546
 
1040
- iterator__clear_entry(entry);
1547
+ if (S_ISDIR(entry->st.st_mode)) {
1548
+ *is_dir = 1;
1549
+ goto done;
1550
+ }
1041
1551
 
1042
- /* Allow you to explicitly advance into a commit/submodule (as well as a
1043
- * tree) to avoid cases where an entry is mislabeled as a submodule in
1044
- * the working directory. The fs iterator will never have COMMMIT
1045
- * entries on it's own, but a wrapper might add them.
1046
- */
1047
- if (fi->entry.path != NULL &&
1048
- (fi->entry.mode == GIT_FILEMODE_TREE ||
1049
- fi->entry.mode == GIT_FILEMODE_COMMIT))
1050
- /* returns GIT_ENOTFOUND if the directory is empty */
1051
- error = fs_iterator__expand_dir(fi);
1552
+ if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) {
1553
+ *is_dir = 0;
1554
+ goto done;
1555
+ }
1052
1556
 
1053
- if (!error && entry)
1054
- error = fs_iterator__current(entry, iter);
1557
+ if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
1558
+ (error = p_stat(fullpath.ptr, &st)) < 0)
1559
+ goto done;
1055
1560
 
1056
- if (!error && !fi->entry.path)
1057
- error = GIT_ITEROVER;
1561
+ *is_dir = S_ISDIR(st.st_mode);
1058
1562
 
1563
+ done:
1564
+ git_buf_dispose(&fullpath);
1059
1565
  return error;
1060
1566
  }
1061
1567
 
1062
- static int fs_iterator__advance_over(
1063
- const git_index_entry **entry, git_iterator *self)
1568
+ static int filesystem_iterator_advance(
1569
+ const git_index_entry **out, git_iterator *i)
1064
1570
  {
1571
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1572
+ bool is_dir;
1065
1573
  int error = 0;
1066
- fs_iterator *fi = (fs_iterator *)self;
1067
- fs_iterator_frame *ff;
1068
- git_path_with_stat *next;
1069
1574
 
1070
- if (entry != NULL)
1071
- *entry = NULL;
1575
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1072
1576
 
1073
- while (fi->entry.path != NULL) {
1074
- ff = fi->stack;
1075
- next = git_vector_get(&ff->entries, ++ff->index);
1577
+ /* examine filesystem entries until we find the next one to return */
1578
+ while (true) {
1579
+ filesystem_iterator_frame *frame;
1580
+ filesystem_iterator_entry *entry;
1076
1581
 
1077
- if (next != NULL)
1582
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL) {
1583
+ error = GIT_ITEROVER;
1078
1584
  break;
1585
+ }
1079
1586
 
1080
- fs_iterator__pop_frame(fi, ff, false);
1081
- }
1587
+ /* no more entries in this frame. pop the frame out */
1588
+ if (frame->next_idx == frame->entries.length) {
1589
+ filesystem_iterator_frame_pop(iter);
1590
+ continue;
1591
+ }
1082
1592
 
1083
- error = fs_iterator__update_entry(fi);
1593
+ /* we have more entries in the current frame, that's our next entry */
1594
+ entry = frame->entries.contents[frame->next_idx];
1595
+ frame->next_idx++;
1084
1596
 
1085
- if (!error && entry != NULL)
1086
- error = fs_iterator__current(entry, self);
1597
+ if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0)
1598
+ break;
1599
+
1600
+ if (is_dir) {
1601
+ if (iterator__do_autoexpand(iter)) {
1602
+ error = filesystem_iterator_frame_push(iter, entry);
1603
+
1604
+ /* may get GIT_ENOTFOUND due to races or permission problems
1605
+ * that we want to quietly swallow
1606
+ */
1607
+ if (error == GIT_ENOTFOUND)
1608
+ continue;
1609
+ else if (error < 0)
1610
+ break;
1611
+ }
1612
+
1613
+ if (!iterator__include_trees(iter))
1614
+ continue;
1615
+ }
1616
+
1617
+ filesystem_iterator_set_current(iter, entry);
1618
+ break;
1619
+ }
1620
+
1621
+ if (out)
1622
+ *out = (error == 0) ? &iter->entry : NULL;
1087
1623
 
1088
1624
  return error;
1089
1625
  }
1090
1626
 
1091
- static int fs_iterator__advance(
1092
- const git_index_entry **entry, git_iterator *self)
1627
+ static int filesystem_iterator_advance_into(
1628
+ const git_index_entry **out, git_iterator *i)
1093
1629
  {
1094
- fs_iterator *fi = (fs_iterator *)self;
1630
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1631
+ filesystem_iterator_frame *frame;
1632
+ filesystem_iterator_entry *prev_entry;
1633
+ int error;
1634
+
1635
+ if (out)
1636
+ *out = NULL;
1637
+
1638
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL)
1639
+ return GIT_ITEROVER;
1640
+
1641
+ /* get the last seen entry */
1642
+ prev_entry = filesystem_iterator_current_entry(frame);
1095
1643
 
1096
- if (!iterator__has_been_accessed(fi))
1097
- return fs_iterator__current(entry, self);
1644
+ /* it's legal to call advance_into when auto-expand is on. in this case,
1645
+ * we will have pushed a new (empty) frame on to the stack for this
1646
+ * new directory. since it's empty, its current_entry should be null.
1647
+ */
1648
+ assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
1098
1649
 
1099
- /* given include_trees & autoexpand, we might have to go into a tree */
1100
- if (iterator__do_autoexpand(fi) &&
1101
- fi->entry.path != NULL &&
1102
- fi->entry.mode == GIT_FILEMODE_TREE)
1103
- {
1104
- int error = fs_iterator__advance_into(entry, self);
1105
- if (error != GIT_ENOTFOUND)
1650
+ if (prev_entry) {
1651
+ if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
1652
+ !S_ISDIR(prev_entry->st.st_mode))
1653
+ return 0;
1654
+
1655
+ if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0)
1106
1656
  return error;
1107
- /* continue silently past empty directories if autoexpanding */
1108
- giterr_clear();
1109
1657
  }
1110
1658
 
1111
- return fs_iterator__advance_over(entry, self);
1659
+ /* we've advanced into the directory in question, let advance
1660
+ * find the first entry
1661
+ */
1662
+ return filesystem_iterator_advance(out, i);
1112
1663
  }
1113
1664
 
1114
- static int fs_iterator__seek(git_iterator *self, const char *prefix)
1665
+ int git_iterator_current_workdir_path(git_buf **out, git_iterator *i)
1115
1666
  {
1116
- GIT_UNUSED(self);
1117
- GIT_UNUSED(prefix);
1118
- /* pop stack until matching prefix */
1119
- /* find prefix item in current frame */
1120
- /* push subdirectories as deep as possible while matching */
1667
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1668
+ const git_index_entry *entry;
1669
+
1670
+ if (i->type != GIT_ITERATOR_TYPE_FS &&
1671
+ i->type != GIT_ITERATOR_TYPE_WORKDIR) {
1672
+ *out = NULL;
1673
+ return 0;
1674
+ }
1675
+
1676
+ git_buf_truncate(&iter->current_path, iter->root_len);
1677
+
1678
+ if (git_iterator_current(&entry, i) < 0 ||
1679
+ git_buf_puts(&iter->current_path, entry->path) < 0)
1680
+ return -1;
1681
+
1682
+ *out = &iter->current_path;
1121
1683
  return 0;
1122
1684
  }
1123
1685
 
1124
- static int fs_iterator__reset(
1125
- git_iterator *self, const char *start, const char *end)
1686
+ GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry)
1126
1687
  {
1127
- int error;
1128
- fs_iterator *fi = (fs_iterator *)self;
1688
+ #if defined(GIT_WIN32) && !defined(__MINGW32__)
1689
+ return (entry && entry->mode) ?
1690
+ (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) :
1691
+ GIT_DIR_FLAG_UNKNOWN;
1692
+ #else
1693
+ GIT_UNUSED(entry);
1694
+ return GIT_DIR_FLAG_UNKNOWN;
1695
+ #endif
1696
+ }
1129
1697
 
1130
- while (fi->stack != NULL && fi->stack->next != NULL)
1131
- fs_iterator__pop_frame(fi, fi->stack, false);
1132
- fi->depth = 0;
1698
+ static void filesystem_iterator_update_ignored(filesystem_iterator *iter)
1699
+ {
1700
+ filesystem_iterator_frame *frame;
1701
+ git_dir_flag dir_flag = entry_dir_flag(&iter->entry);
1133
1702
 
1134
- if ((error = iterator__reset_range(self, start, end)) < 0)
1135
- return error;
1703
+ if (git_ignore__lookup(&iter->current_is_ignored,
1704
+ &iter->ignores, iter->entry.path, dir_flag) < 0) {
1705
+ git_error_clear();
1706
+ iter->current_is_ignored = GIT_IGNORE_NOTFOUND;
1707
+ }
1136
1708
 
1137
- fs_iterator__seek_frame_start(fi, fi->stack);
1709
+ /* use ignore from containing frame stack */
1710
+ if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) {
1711
+ frame = filesystem_iterator_current_frame(iter);
1712
+ iter->current_is_ignored = frame->is_ignored;
1713
+ }
1714
+ }
1138
1715
 
1139
- error = fs_iterator__update_entry(fi);
1140
- if (error == GIT_ITEROVER)
1141
- error = 0;
1716
+ GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
1717
+ filesystem_iterator *iter)
1718
+ {
1719
+ if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED)
1720
+ filesystem_iterator_update_ignored(iter);
1142
1721
 
1143
- return error;
1722
+ return (iter->current_is_ignored == GIT_IGNORE_TRUE);
1144
1723
  }
1145
1724
 
1146
- static void fs_iterator__free(git_iterator *self)
1725
+ bool git_iterator_current_is_ignored(git_iterator *i)
1147
1726
  {
1148
- fs_iterator *fi = (fs_iterator *)self;
1149
-
1150
- while (fi->stack != NULL)
1151
- fs_iterator__pop_frame(fi, fi->stack, true);
1727
+ if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
1728
+ return false;
1152
1729
 
1153
- git_buf_free(&fi->path);
1730
+ return filesystem_iterator_current_is_ignored((filesystem_iterator *)i);
1154
1731
  }
1155
1732
 
1156
- static int fs_iterator__update_entry(fs_iterator *fi)
1733
+ bool git_iterator_current_tree_is_ignored(git_iterator *i)
1157
1734
  {
1158
- git_path_with_stat *ps;
1735
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1736
+ filesystem_iterator_frame *frame;
1159
1737
 
1160
- memset(&fi->entry, 0, sizeof(fi->entry));
1738
+ if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
1739
+ return false;
1161
1740
 
1162
- if (!fi->stack)
1163
- return GIT_ITEROVER;
1741
+ frame = filesystem_iterator_current_frame(iter);
1742
+ return (frame->is_ignored == GIT_IGNORE_TRUE);
1743
+ }
1164
1744
 
1165
- ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1166
- if (!ps)
1167
- return GIT_ITEROVER;
1745
+ static int filesystem_iterator_advance_over(
1746
+ const git_index_entry **out,
1747
+ git_iterator_status_t *status,
1748
+ git_iterator *i)
1749
+ {
1750
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1751
+ filesystem_iterator_frame *current_frame;
1752
+ filesystem_iterator_entry *current_entry;
1753
+ const git_index_entry *entry = NULL;
1754
+ const char *base;
1755
+ int error = 0;
1168
1756
 
1169
- git_buf_truncate(&fi->path, fi->root_len);
1170
- if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1171
- return -1;
1757
+ *out = NULL;
1758
+ *status = GIT_ITERATOR_STATUS_NORMAL;
1172
1759
 
1173
- if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1174
- return GIT_ITEROVER;
1760
+ assert(iterator__has_been_accessed(i));
1175
1761
 
1176
- fi->entry.path = ps->path;
1177
- git_index_entry__init_from_stat(&fi->entry, &ps->st);
1762
+ current_frame = filesystem_iterator_current_frame(iter);
1763
+ assert(current_frame);
1764
+ current_entry = filesystem_iterator_current_entry(current_frame);
1765
+ assert(current_entry);
1178
1766
 
1179
- /* need different mode here to keep directories during iteration */
1180
- fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1767
+ if ((error = git_iterator_current(&entry, i)) < 0)
1768
+ return error;
1181
1769
 
1182
- /* allow wrapper to check/update the entry (can force skip) */
1183
- if (fi->update_entry_cb &&
1184
- fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1185
- return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1770
+ if (!S_ISDIR(entry->mode)) {
1771
+ if (filesystem_iterator_current_is_ignored(iter))
1772
+ *status = GIT_ITERATOR_STATUS_IGNORED;
1186
1773
 
1187
- /* if this is a tree and trees aren't included, then skip */
1188
- if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1189
- int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1190
- if (error != GIT_ENOTFOUND)
1191
- return error;
1192
- giterr_clear();
1193
- return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1774
+ return filesystem_iterator_advance(out, i);
1194
1775
  }
1195
1776
 
1196
- return 0;
1197
- }
1777
+ git_buf_clear(&iter->tmp_buf);
1778
+ if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0)
1779
+ return error;
1198
1780
 
1199
- static int fs_iterator__initialize(
1200
- git_iterator **out, fs_iterator *fi, const char *root)
1201
- {
1202
- int error;
1781
+ base = iter->tmp_buf.ptr;
1782
+
1783
+ /* scan inside the directory looking for files. if we find nothing,
1784
+ * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
1785
+ * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
1786
+ * and then stop.
1787
+ *
1788
+ * however, if we're here looking for a pathlist item (but are not
1789
+ * actually in the pathlist ourselves) then start at FILTERED instead of
1790
+ * EMPTY. callers then know that this path was not something they asked
1791
+ * about.
1792
+ */
1793
+ *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ?
1794
+ GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY;
1795
+
1796
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
1797
+ if (filesystem_iterator_current_is_ignored(iter)) {
1798
+ /* if we found an explicitly ignored item, then update from
1799
+ * EMPTY to IGNORED
1800
+ */
1801
+ *status = GIT_ITERATOR_STATUS_IGNORED;
1802
+ } else if (S_ISDIR(entry->mode)) {
1803
+ error = filesystem_iterator_advance_into(&entry, i);
1804
+
1805
+ if (!error)
1806
+ continue;
1807
+
1808
+ /* this directory disappeared, ignore it */
1809
+ else if (error == GIT_ENOTFOUND)
1810
+ error = 0;
1811
+
1812
+ /* a real error occurred */
1813
+ else
1814
+ break;
1815
+ } else {
1816
+ /* we found a non-ignored item, treat parent as untracked */
1817
+ *status = GIT_ITERATOR_STATUS_NORMAL;
1818
+ break;
1819
+ }
1203
1820
 
1204
- if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1205
- git__free(fi);
1206
- return -1;
1821
+ if ((error = git_iterator_advance(&entry, i)) < 0)
1822
+ break;
1207
1823
  }
1208
- fi->root_len = fi->path.size;
1209
1824
 
1210
- if ((error = fs_iterator__expand_dir(fi)) < 0) {
1211
- if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1212
- giterr_clear();
1213
- error = 0;
1214
- } else {
1215
- git_iterator_free((git_iterator *)fi);
1216
- fi = NULL;
1217
- }
1825
+ /* wrap up scan back to base directory */
1826
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
1827
+ if ((error = git_iterator_advance(&entry, i)) < 0)
1828
+ break;
1218
1829
  }
1219
1830
 
1220
- *out = (git_iterator *)fi;
1831
+ if (!error)
1832
+ *out = entry;
1833
+
1221
1834
  return error;
1222
1835
  }
1223
1836
 
1224
- int git_iterator_for_filesystem(
1225
- git_iterator **out,
1226
- const char *root,
1227
- git_iterator_flag_t flags,
1228
- const char *start,
1229
- const char *end)
1837
+ static void filesystem_iterator_clear(filesystem_iterator *iter)
1230
1838
  {
1231
- fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1232
- GITERR_CHECK_ALLOC(fi);
1839
+ while (iter->frames.size)
1840
+ filesystem_iterator_frame_pop(iter);
1233
1841
 
1234
- ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1842
+ git_array_clear(iter->frames);
1843
+ git_ignore__free(&iter->ignores);
1235
1844
 
1236
- if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1237
- fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1845
+ git_buf_dispose(&iter->tmp_buf);
1238
1846
 
1239
- return fs_iterator__initialize(out, fi, root);
1847
+ iterator_clear(&iter->base);
1240
1848
  }
1241
1849
 
1242
-
1243
- typedef struct {
1244
- fs_iterator fi;
1245
- git_ignores ignores;
1246
- int is_ignored;
1247
- } workdir_iterator;
1248
-
1249
- GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1850
+ static int filesystem_iterator_init(filesystem_iterator *iter)
1250
1851
  {
1251
- size_t len;
1852
+ int error;
1252
1853
 
1253
- if (!path || (len = path->size) < 4)
1254
- return false;
1854
+ if (iterator__honor_ignores(&iter->base) &&
1855
+ (error = git_ignore__for_path(iter->base.repo,
1856
+ ".gitignore", &iter->ignores)) < 0)
1857
+ return error;
1255
1858
 
1256
- if (path->ptr[len - 1] == '/')
1257
- len--;
1859
+ if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0)
1860
+ return error;
1258
1861
 
1259
- if (tolower(path->ptr[len - 1]) != 't' ||
1260
- tolower(path->ptr[len - 2]) != 'i' ||
1261
- tolower(path->ptr[len - 3]) != 'g' ||
1262
- tolower(path->ptr[len - 4]) != '.')
1263
- return false;
1862
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1264
1863
 
1265
- return (len == 4 || path->ptr[len - 5] == '/');
1864
+ return 0;
1266
1865
  }
1267
1866
 
1268
- static int workdir_iterator__enter_dir(fs_iterator *fi)
1867
+ static int filesystem_iterator_reset(git_iterator *i)
1269
1868
  {
1270
- /* only push new ignores if this is not top level directory */
1271
- if (fi->stack->next != NULL) {
1272
- workdir_iterator *wi = (workdir_iterator *)fi;
1273
- ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1274
-
1275
- (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1276
- }
1869
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1277
1870
 
1278
- return 0;
1871
+ filesystem_iterator_clear(iter);
1872
+ return filesystem_iterator_init(iter);
1279
1873
  }
1280
1874
 
1281
- static int workdir_iterator__leave_dir(fs_iterator *fi)
1875
+ static void filesystem_iterator_free(git_iterator *i)
1282
1876
  {
1283
- workdir_iterator *wi = (workdir_iterator *)fi;
1284
- git_ignore__pop_dir(&wi->ignores);
1285
- return 0;
1877
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1878
+ git__free(iter->root);
1879
+ git_buf_dispose(&iter->current_path);
1880
+ git_tree_free(iter->tree);
1881
+ if (iter->index)
1882
+ git_index_snapshot_release(&iter->index_snapshot, iter->index);
1883
+ filesystem_iterator_clear(iter);
1286
1884
  }
1287
1885
 
1288
- static int workdir_iterator__update_entry(fs_iterator *fi)
1886
+ static int iterator_for_filesystem(
1887
+ git_iterator **out,
1888
+ git_repository *repo,
1889
+ const char *root,
1890
+ git_index *index,
1891
+ git_tree *tree,
1892
+ git_iterator_type_t type,
1893
+ git_iterator_options *options)
1289
1894
  {
1290
- int error = 0;
1291
- workdir_iterator *wi = (workdir_iterator *)fi;
1895
+ filesystem_iterator *iter;
1896
+ size_t root_len;
1897
+ int error;
1292
1898
 
1293
- /* skip over .git entries */
1294
- if (workdir_path_is_dotgit(&fi->path))
1295
- return GIT_ENOTFOUND;
1899
+ static git_iterator_callbacks callbacks = {
1900
+ filesystem_iterator_current,
1901
+ filesystem_iterator_advance,
1902
+ filesystem_iterator_advance_into,
1903
+ filesystem_iterator_advance_over,
1904
+ filesystem_iterator_reset,
1905
+ filesystem_iterator_free
1906
+ };
1296
1907
 
1297
- /* reset is_ignored since we haven't checked yet */
1298
- wi->is_ignored = -1;
1908
+ *out = NULL;
1299
1909
 
1300
- /* check if apparent tree entries are actually submodules */
1301
- if (fi->entry.mode != GIT_FILEMODE_TREE)
1302
- return 0;
1910
+ if (root == NULL)
1911
+ return git_iterator_for_nothing(out, options);
1303
1912
 
1304
- error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
1305
- if (error < 0)
1306
- giterr_clear();
1913
+ iter = git__calloc(1, sizeof(filesystem_iterator));
1914
+ GIT_ERROR_CHECK_ALLOC(iter);
1307
1915
 
1308
- /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1309
- if (!error || error == GIT_EEXISTS) {
1310
- fi->entry.mode = S_IFGITLINK;
1311
- fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
1916
+ iter->base.type = type;
1917
+ iter->base.cb = &callbacks;
1918
+
1919
+ root_len = strlen(root);
1920
+
1921
+ iter->root = git__malloc(root_len+2);
1922
+ GIT_ERROR_CHECK_ALLOC(iter->root);
1923
+
1924
+ memcpy(iter->root, root, root_len);
1925
+
1926
+ if (root_len == 0 || root[root_len-1] != '/') {
1927
+ iter->root[root_len] = '/';
1928
+ root_len++;
1312
1929
  }
1930
+ iter->root[root_len] = '\0';
1931
+ iter->root_len = root_len;
1932
+
1933
+ if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0)
1934
+ goto on_error;
1935
+
1936
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0)
1937
+ goto on_error;
1938
+
1939
+ if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0)
1940
+ goto on_error;
1313
1941
 
1942
+ if (index &&
1943
+ (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0)
1944
+ goto on_error;
1945
+
1946
+ iter->index = index;
1947
+ iter->dirload_flags =
1948
+ (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1949
+ (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
1950
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1951
+
1952
+ if ((error = filesystem_iterator_init(iter)) < 0)
1953
+ goto on_error;
1954
+
1955
+ *out = &iter->base;
1314
1956
  return 0;
1957
+
1958
+ on_error:
1959
+ git_iterator_free(&iter->base);
1960
+ return error;
1315
1961
  }
1316
1962
 
1317
- static void workdir_iterator__free(git_iterator *self)
1963
+ int git_iterator_for_filesystem(
1964
+ git_iterator **out,
1965
+ const char *root,
1966
+ git_iterator_options *options)
1318
1967
  {
1319
- workdir_iterator *wi = (workdir_iterator *)self;
1320
- fs_iterator__free(self);
1321
- git_ignore__free(&wi->ignores);
1968
+ return iterator_for_filesystem(out,
1969
+ NULL, root, NULL, NULL, GIT_ITERATOR_TYPE_FS, options);
1322
1970
  }
1323
1971
 
1324
1972
  int git_iterator_for_workdir_ext(
1325
1973
  git_iterator **out,
1326
1974
  git_repository *repo,
1327
1975
  const char *repo_workdir,
1328
- git_iterator_flag_t flags,
1329
- const char *start,
1330
- const char *end)
1976
+ git_index *index,
1977
+ git_tree *tree,
1978
+ git_iterator_options *given_opts)
1331
1979
  {
1332
- int error;
1333
- workdir_iterator *wi;
1980
+ git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
1334
1981
 
1335
1982
  if (!repo_workdir) {
1336
1983
  if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1337
1984
  return GIT_EBAREREPO;
1985
+
1338
1986
  repo_workdir = git_repository_workdir(repo);
1339
1987
  }
1340
1988
 
1341
- /* initialize as an fs iterator then do overrides */
1342
- wi = git__calloc(1, sizeof(workdir_iterator));
1343
- GITERR_CHECK_ALLOC(wi);
1344
- ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1345
-
1346
- wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1347
- wi->fi.cb.free = workdir_iterator__free;
1348
- wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1349
- wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1350
- wi->fi.update_entry_cb = workdir_iterator__update_entry;
1351
-
1352
- if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1353
- (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
1354
- {
1355
- git_iterator_free((git_iterator *)wi);
1356
- return error;
1357
- }
1989
+ /* upgrade to a workdir iterator, adding necessary internal flags */
1990
+ if (given_opts)
1991
+ memcpy(&options, given_opts, sizeof(git_iterator_options));
1358
1992
 
1359
- return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1993
+ options.flags |= GIT_ITERATOR_HONOR_IGNORES |
1994
+ GIT_ITERATOR_IGNORE_DOT_GIT;
1995
+
1996
+ return iterator_for_filesystem(out,
1997
+ repo, repo_workdir, index, tree, GIT_ITERATOR_TYPE_WORKDIR, &options);
1360
1998
  }
1361
1999
 
1362
2000
 
1363
- void git_iterator_free(git_iterator *iter)
2001
+ /* Index iterator */
2002
+
2003
+
2004
+ typedef struct {
2005
+ git_iterator base;
2006
+ git_vector entries;
2007
+ size_t next_idx;
2008
+
2009
+ /* the pseudotree entry */
2010
+ git_index_entry tree_entry;
2011
+ git_buf tree_buf;
2012
+ bool skip_tree;
2013
+
2014
+ const git_index_entry *entry;
2015
+ } index_iterator;
2016
+
2017
+ static int index_iterator_current(
2018
+ const git_index_entry **out, git_iterator *i)
1364
2019
  {
1365
- if (iter == NULL)
1366
- return;
2020
+ index_iterator *iter = (index_iterator *)i;
1367
2021
 
1368
- iter->cb->free(iter);
2022
+ if (!iterator__has_been_accessed(i))
2023
+ return iter->base.cb->advance(out, i);
1369
2024
 
1370
- git__free(iter->start);
1371
- git__free(iter->end);
2025
+ if (iter->entry == NULL) {
2026
+ *out = NULL;
2027
+ return GIT_ITEROVER;
2028
+ }
1372
2029
 
1373
- memset(iter, 0, sizeof(*iter));
2030
+ *out = iter->entry;
2031
+ return 0;
2032
+ }
1374
2033
 
1375
- git__free(iter);
2034
+ static bool index_iterator_create_pseudotree(
2035
+ const git_index_entry **out,
2036
+ index_iterator *iter,
2037
+ const char *path)
2038
+ {
2039
+ const char *prev_path, *relative_path, *dirsep;
2040
+ size_t common_len;
2041
+
2042
+ prev_path = iter->entry ? iter->entry->path : "";
2043
+
2044
+ /* determine if the new path is in a different directory from the old */
2045
+ common_len = git_path_common_dirlen(prev_path, path);
2046
+ relative_path = path + common_len;
2047
+
2048
+ if ((dirsep = strchr(relative_path, '/')) == NULL)
2049
+ return false;
2050
+
2051
+ git_buf_clear(&iter->tree_buf);
2052
+ git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1);
2053
+
2054
+ iter->tree_entry.mode = GIT_FILEMODE_TREE;
2055
+ iter->tree_entry.path = iter->tree_buf.ptr;
2056
+
2057
+ *out = &iter->tree_entry;
2058
+ return true;
1376
2059
  }
1377
2060
 
1378
- int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
2061
+ static int index_iterator_skip_pseudotree(index_iterator *iter)
1379
2062
  {
1380
- bool desire_ignore_case = (ignore_case != 0);
2063
+ assert(iterator__has_been_accessed(&iter->base));
2064
+ assert(S_ISDIR(iter->entry->mode));
1381
2065
 
1382
- if (iterator__ignore_case(iter) == desire_ignore_case)
1383
- return 0;
2066
+ while (true) {
2067
+ const git_index_entry *next_entry = NULL;
1384
2068
 
1385
- if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1386
- if (desire_ignore_case)
1387
- iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1388
- else
1389
- iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1390
- } else {
1391
- giterr_set(GITERR_INVALID,
1392
- "Cannot currently set ignore case on non-empty iterators");
1393
- return -1;
2069
+ if (++iter->next_idx >= iter->entries.length)
2070
+ return GIT_ITEROVER;
2071
+
2072
+ next_entry = iter->entries.contents[iter->next_idx];
2073
+
2074
+ if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path,
2075
+ iter->tree_buf.size) != 0)
2076
+ break;
1394
2077
  }
1395
2078
 
2079
+ iter->skip_tree = false;
1396
2080
  return 0;
1397
2081
  }
1398
2082
 
1399
- git_index *git_iterator_get_index(git_iterator *iter)
2083
+ static int index_iterator_advance(
2084
+ const git_index_entry **out, git_iterator *i)
1400
2085
  {
1401
- if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1402
- return ((index_iterator *)iter)->index;
1403
- return NULL;
2086
+ index_iterator *iter = (index_iterator *)i;
2087
+ const git_index_entry *entry = NULL;
2088
+ bool is_submodule;
2089
+ int error = 0;
2090
+
2091
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
2092
+
2093
+ while (true) {
2094
+ if (iter->next_idx >= iter->entries.length) {
2095
+ error = GIT_ITEROVER;
2096
+ break;
2097
+ }
2098
+
2099
+ /* we were not asked to expand this pseudotree. advance over it. */
2100
+ if (iter->skip_tree) {
2101
+ index_iterator_skip_pseudotree(iter);
2102
+ continue;
2103
+ }
2104
+
2105
+ entry = iter->entries.contents[iter->next_idx];
2106
+ is_submodule = S_ISGITLINK(entry->mode);
2107
+
2108
+ if (!iterator_has_started(&iter->base, entry->path, is_submodule)) {
2109
+ iter->next_idx++;
2110
+ continue;
2111
+ }
2112
+
2113
+ if (iterator_has_ended(&iter->base, entry->path)) {
2114
+ error = GIT_ITEROVER;
2115
+ break;
2116
+ }
2117
+
2118
+ /* if we have a list of paths we're interested in, examine it */
2119
+ if (!iterator_pathlist_next_is(&iter->base, entry->path)) {
2120
+ iter->next_idx++;
2121
+ continue;
2122
+ }
2123
+
2124
+ /* if this is a conflict, skip it unless we're including conflicts */
2125
+ if (git_index_entry_is_conflict(entry) &&
2126
+ !iterator__include_conflicts(&iter->base)) {
2127
+ iter->next_idx++;
2128
+ continue;
2129
+ }
2130
+
2131
+ /* we've found what will be our next _file_ entry. but if we are
2132
+ * returning trees entries, we may need to return a pseudotree
2133
+ * entry that will contain this. don't advance over this entry,
2134
+ * though, we still need to return it on the next `advance`.
2135
+ */
2136
+ if (iterator__include_trees(&iter->base) &&
2137
+ index_iterator_create_pseudotree(&entry, iter, entry->path)) {
2138
+
2139
+ /* Note whether this pseudo tree should be expanded or not */
2140
+ iter->skip_tree = iterator__dont_autoexpand(&iter->base);
2141
+ break;
2142
+ }
2143
+
2144
+ iter->next_idx++;
2145
+ break;
2146
+ }
2147
+
2148
+ iter->entry = (error == 0) ? entry : NULL;
2149
+
2150
+ if (out)
2151
+ *out = iter->entry;
2152
+
2153
+ return error;
1404
2154
  }
1405
2155
 
1406
- int git_iterator_current_tree_entry(
1407
- const git_tree_entry **tree_entry, git_iterator *iter)
1408
- {
1409
- if (iter->type != GIT_ITERATOR_TYPE_TREE)
1410
- *tree_entry = NULL;
1411
- else {
1412
- tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1413
- *tree_entry = (tf->current < tf->n_entries) ?
1414
- tf->entries[tf->current]->te : NULL;
2156
+ static int index_iterator_advance_into(
2157
+ const git_index_entry **out, git_iterator *i)
2158
+ {
2159
+ index_iterator *iter = (index_iterator *)i;
2160
+
2161
+ if (! S_ISDIR(iter->tree_entry.mode)) {
2162
+ if (out)
2163
+ *out = NULL;
2164
+
2165
+ return 0;
1415
2166
  }
1416
2167
 
2168
+ iter->skip_tree = false;
2169
+ return index_iterator_advance(out, i);
2170
+ }
2171
+
2172
+ static int index_iterator_advance_over(
2173
+ const git_index_entry **out,
2174
+ git_iterator_status_t *status,
2175
+ git_iterator *i)
2176
+ {
2177
+ index_iterator *iter = (index_iterator *)i;
2178
+ const git_index_entry *entry;
2179
+ int error;
2180
+
2181
+ if ((error = index_iterator_current(&entry, i)) < 0)
2182
+ return error;
2183
+
2184
+ if (S_ISDIR(entry->mode))
2185
+ index_iterator_skip_pseudotree(iter);
2186
+
2187
+ *status = GIT_ITERATOR_STATUS_NORMAL;
2188
+ return index_iterator_advance(out, i);
2189
+ }
2190
+
2191
+ static void index_iterator_clear(index_iterator *iter)
2192
+ {
2193
+ iterator_clear(&iter->base);
2194
+ }
2195
+
2196
+ static int index_iterator_init(index_iterator *iter)
2197
+ {
2198
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
2199
+ iter->next_idx = 0;
2200
+ iter->skip_tree = false;
1417
2201
  return 0;
1418
2202
  }
1419
2203
 
1420
- int git_iterator_current_parent_tree(
1421
- const git_tree **tree_ptr,
1422
- git_iterator *iter,
1423
- const char *parent_path)
2204
+ static int index_iterator_reset(git_iterator *i)
1424
2205
  {
1425
- tree_iterator *ti = (tree_iterator *)iter;
1426
- tree_iterator_frame *tf;
1427
- const char *scan = parent_path;
1428
- const git_tree_entry *te;
2206
+ index_iterator *iter = (index_iterator *)i;
1429
2207
 
1430
- *tree_ptr = NULL;
2208
+ index_iterator_clear(iter);
2209
+ return index_iterator_init(iter);
2210
+ }
1431
2211
 
1432
- if (iter->type != GIT_ITERATOR_TYPE_TREE)
1433
- return 0;
2212
+ static void index_iterator_free(git_iterator *i)
2213
+ {
2214
+ index_iterator *iter = (index_iterator *)i;
1434
2215
 
1435
- for (tf = ti->root; *scan; ) {
1436
- if (!(tf = tf->down) ||
1437
- tf->current >= tf->n_entries ||
1438
- !(te = tf->entries[tf->current]->te) ||
1439
- ti->strncomp(scan, te->filename, te->filename_len) != 0)
1440
- return 0;
2216
+ git_index_snapshot_release(&iter->entries, iter->base.index);
2217
+ git_buf_dispose(&iter->tree_buf);
2218
+ }
1441
2219
 
1442
- scan += te->filename_len;
1443
- if (*scan == '/')
1444
- scan++;
1445
- }
2220
+ int git_iterator_for_index(
2221
+ git_iterator **out,
2222
+ git_repository *repo,
2223
+ git_index *index,
2224
+ git_iterator_options *options)
2225
+ {
2226
+ index_iterator *iter;
2227
+ int error;
2228
+
2229
+ static git_iterator_callbacks callbacks = {
2230
+ index_iterator_current,
2231
+ index_iterator_advance,
2232
+ index_iterator_advance_into,
2233
+ index_iterator_advance_over,
2234
+ index_iterator_reset,
2235
+ index_iterator_free
2236
+ };
2237
+
2238
+ *out = NULL;
2239
+
2240
+ if (index == NULL)
2241
+ return git_iterator_for_nothing(out, options);
2242
+
2243
+ iter = git__calloc(1, sizeof(index_iterator));
2244
+ GIT_ERROR_CHECK_ALLOC(iter);
2245
+
2246
+ iter->base.type = GIT_ITERATOR_TYPE_INDEX;
2247
+ iter->base.cb = &callbacks;
2248
+
2249
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 ||
2250
+ (error = git_index_snapshot_new(&iter->entries, index)) < 0 ||
2251
+ (error = index_iterator_init(iter)) < 0)
2252
+ goto on_error;
2253
+
2254
+ git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ?
2255
+ git_index_entry_icmp : git_index_entry_cmp);
2256
+ git_vector_sort(&iter->entries);
1446
2257
 
1447
- *tree_ptr = tf->entries[tf->current]->tree;
2258
+ *out = &iter->base;
1448
2259
  return 0;
2260
+
2261
+ on_error:
2262
+ git_iterator_free(&iter->base);
2263
+ return error;
1449
2264
  }
1450
2265
 
1451
- bool git_iterator_current_is_ignored(git_iterator *iter)
2266
+
2267
+ /* Iterator API */
2268
+
2269
+ int git_iterator_reset_range(
2270
+ git_iterator *i, const char *start, const char *end)
1452
2271
  {
1453
- workdir_iterator *wi = (workdir_iterator *)iter;
2272
+ if (iterator_reset_range(i, start, end) < 0)
2273
+ return -1;
1454
2274
 
1455
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1456
- return false;
2275
+ return i->cb->reset(i);
2276
+ }
1457
2277
 
1458
- if (wi->is_ignored != -1)
1459
- return (bool)(wi->is_ignored != 0);
2278
+ void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
2279
+ {
2280
+ assert(!iterator__has_been_accessed(i));
2281
+ iterator_set_ignore_case(i, ignore_case);
2282
+ }
1460
2283
 
1461
- if (git_ignore__lookup(
1462
- &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1463
- wi->is_ignored = true;
2284
+ void git_iterator_free(git_iterator *iter)
2285
+ {
2286
+ if (iter == NULL)
2287
+ return;
1464
2288
 
1465
- return (bool)wi->is_ignored;
2289
+ iter->cb->free(iter);
2290
+
2291
+ git_vector_free(&iter->pathlist);
2292
+ git__free(iter->start);
2293
+ git__free(iter->end);
2294
+
2295
+ memset(iter, 0, sizeof(*iter));
2296
+
2297
+ git__free(iter);
1466
2298
  }
1467
2299
 
1468
- int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
2300
+ int git_iterator_foreach(
2301
+ git_iterator *iterator,
2302
+ git_iterator_foreach_cb cb,
2303
+ void *data)
1469
2304
  {
1470
- const git_index_entry *entry;
2305
+ const git_index_entry *iterator_item;
2306
+ int error = 0;
1471
2307
 
1472
- /* a "done" iterator is after every prefix */
1473
- if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
1474
- return 1;
2308
+ if ((error = git_iterator_current(&iterator_item, iterator)) < 0)
2309
+ goto done;
1475
2310
 
1476
- /* a NULL prefix is after any valid iterator */
1477
- if (!path_prefix)
1478
- return -1;
2311
+ if ((error = cb(iterator_item, data)) != 0)
2312
+ goto done;
2313
+
2314
+ while (true) {
2315
+ if ((error = git_iterator_advance(&iterator_item, iterator)) < 0)
2316
+ goto done;
2317
+
2318
+ if ((error = cb(iterator_item, data)) != 0)
2319
+ goto done;
2320
+ }
1479
2321
 
1480
- return iter->prefixcomp(entry->path, path_prefix);
2322
+ done:
2323
+ if (error == GIT_ITEROVER)
2324
+ error = 0;
2325
+
2326
+ return error;
1481
2327
  }
1482
2328
 
1483
- int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
2329
+ int git_iterator_walk(
2330
+ git_iterator **iterators,
2331
+ size_t cnt,
2332
+ git_iterator_walk_cb cb,
2333
+ void *data)
1484
2334
  {
1485
- workdir_iterator *wi = (workdir_iterator *)iter;
2335
+ const git_index_entry **iterator_item; /* next in each iterator */
2336
+ const git_index_entry **cur_items; /* current path in each iter */
2337
+ const git_index_entry *first_match;
2338
+ size_t i, j;
2339
+ int error = 0;
1486
2340
 
1487
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1488
- *path = NULL;
1489
- else
1490
- *path = &wi->fi.path;
2341
+ iterator_item = git__calloc(cnt, sizeof(git_index_entry *));
2342
+ cur_items = git__calloc(cnt, sizeof(git_index_entry *));
1491
2343
 
1492
- return 0;
2344
+ GIT_ERROR_CHECK_ALLOC(iterator_item);
2345
+ GIT_ERROR_CHECK_ALLOC(cur_items);
2346
+
2347
+ /* Set up the iterators */
2348
+ for (i = 0; i < cnt; i++) {
2349
+ error = git_iterator_current(&iterator_item[i], iterators[i]);
2350
+
2351
+ if (error < 0 && error != GIT_ITEROVER)
2352
+ goto done;
2353
+ }
2354
+
2355
+ while (true) {
2356
+ for (i = 0; i < cnt; i++)
2357
+ cur_items[i] = NULL;
2358
+
2359
+ first_match = NULL;
2360
+
2361
+ /* Find the next path(s) to consume from each iterator */
2362
+ for (i = 0; i < cnt; i++) {
2363
+ if (iterator_item[i] == NULL)
2364
+ continue;
2365
+
2366
+ if (first_match == NULL) {
2367
+ first_match = iterator_item[i];
2368
+ cur_items[i] = iterator_item[i];
2369
+ } else {
2370
+ int path_diff = git_index_entry_cmp(iterator_item[i], first_match);
2371
+
2372
+ if (path_diff < 0) {
2373
+ /* Found an index entry that sorts before the one we're
2374
+ * looking at. Forget that we've seen the other and
2375
+ * look at the other iterators for this path.
2376
+ */
2377
+ for (j = 0; j < i; j++)
2378
+ cur_items[j] = NULL;
2379
+
2380
+ first_match = iterator_item[i];
2381
+ cur_items[i] = iterator_item[i];
2382
+ } else if (path_diff == 0) {
2383
+ cur_items[i] = iterator_item[i];
2384
+ }
2385
+ }
2386
+ }
2387
+
2388
+ if (first_match == NULL)
2389
+ break;
2390
+
2391
+ if ((error = cb(cur_items, data)) != 0)
2392
+ goto done;
2393
+
2394
+ /* Advance each iterator that participated */
2395
+ for (i = 0; i < cnt; i++) {
2396
+ if (cur_items[i] == NULL)
2397
+ continue;
2398
+
2399
+ error = git_iterator_advance(&iterator_item[i], iterators[i]);
2400
+
2401
+ if (error < 0 && error != GIT_ITEROVER)
2402
+ goto done;
2403
+ }
2404
+ }
2405
+
2406
+ done:
2407
+ git__free((git_index_entry **)iterator_item);
2408
+ git__free((git_index_entry **)cur_items);
2409
+
2410
+ if (error == GIT_ITEROVER)
2411
+ error = 0;
2412
+
2413
+ return error;
1493
2414
  }