rugged 0.17.0.b7 → 0.18.0.b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +88 -32
  3. data/ext/rugged/extconf.rb +4 -2
  4. data/ext/rugged/rugged.c +72 -10
  5. data/ext/rugged/rugged.h +14 -10
  6. data/ext/rugged/rugged_blob.c +8 -10
  7. data/ext/rugged/rugged_branch.c +11 -14
  8. data/ext/rugged/rugged_commit.c +31 -24
  9. data/ext/rugged/rugged_config.c +2 -2
  10. data/ext/rugged/rugged_index.c +133 -198
  11. data/ext/rugged/rugged_note.c +372 -0
  12. data/ext/rugged/rugged_object.c +50 -22
  13. data/ext/rugged/rugged_reference.c +122 -130
  14. data/ext/rugged/rugged_remote.c +72 -29
  15. data/ext/rugged/rugged_repo.c +402 -20
  16. data/ext/rugged/rugged_revwalk.c +7 -3
  17. data/ext/rugged/rugged_settings.c +110 -0
  18. data/ext/rugged/rugged_signature.c +23 -7
  19. data/ext/rugged/rugged_tag.c +32 -16
  20. data/ext/rugged/rugged_tree.c +44 -15
  21. data/lib/rugged.rb +1 -0
  22. data/lib/rugged/index.rb +8 -0
  23. data/lib/rugged/remote.rb +13 -0
  24. data/lib/rugged/repository.rb +3 -3
  25. data/lib/rugged/version.rb +1 -1
  26. data/test/blob_test.rb +13 -15
  27. data/test/branch_test.rb +32 -67
  28. data/test/commit_test.rb +50 -12
  29. data/test/config_test.rb +12 -11
  30. data/test/coverage/HEAD.json +1 -1
  31. data/test/coverage/cover.rb +40 -21
  32. data/test/errors_test.rb +34 -0
  33. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  34. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  35. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  36. data/test/fixtures/testrepo.git/logs/refs/notes/commits +1 -0
  37. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +1 -0
  38. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  39. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +1 -0
  40. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  41. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  42. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +3 -0
  43. data/test/fixtures/testrepo.git/refs/notes/commits +1 -0
  44. data/test/index_test.rb +65 -69
  45. data/test/lib_test.rb +76 -11
  46. data/test/note_test.rb +158 -0
  47. data/test/object_test.rb +8 -11
  48. data/test/reference_test.rb +77 -85
  49. data/test/remote_test.rb +86 -8
  50. data/test/repo_pack_test.rb +9 -7
  51. data/test/repo_reset_test.rb +80 -0
  52. data/test/repo_test.rb +176 -53
  53. data/test/tag_test.rb +44 -7
  54. data/test/test_helper.rb +63 -35
  55. data/test/tree_test.rb +34 -13
  56. data/test/walker_test.rb +14 -14
  57. data/vendor/libgit2/Makefile.embed +1 -1
  58. data/vendor/libgit2/deps/http-parser/http_parser.c +974 -578
  59. data/vendor/libgit2/deps/http-parser/http_parser.h +106 -70
  60. data/vendor/libgit2/deps/regex/regcomp.c +7 -6
  61. data/vendor/libgit2/deps/regex/regex_internal.c +1 -1
  62. data/vendor/libgit2/deps/regex/regex_internal.h +12 -3
  63. data/vendor/libgit2/deps/regex/regexec.c +5 -5
  64. data/vendor/libgit2/include/git2.h +5 -1
  65. data/vendor/libgit2/include/git2/attr.h +4 -2
  66. data/vendor/libgit2/include/git2/blob.h +39 -12
  67. data/vendor/libgit2/include/git2/branch.h +123 -35
  68. data/vendor/libgit2/include/git2/checkout.h +206 -48
  69. data/vendor/libgit2/include/git2/clone.h +72 -27
  70. data/vendor/libgit2/include/git2/commit.h +20 -17
  71. data/vendor/libgit2/include/git2/common.h +67 -1
  72. data/vendor/libgit2/include/git2/config.h +81 -60
  73. data/vendor/libgit2/include/git2/cred_helpers.h +53 -0
  74. data/vendor/libgit2/include/git2/diff.h +459 -150
  75. data/vendor/libgit2/include/git2/errors.h +9 -1
  76. data/vendor/libgit2/include/git2/graph.h +41 -0
  77. data/vendor/libgit2/include/git2/ignore.h +7 -6
  78. data/vendor/libgit2/include/git2/index.h +323 -97
  79. data/vendor/libgit2/include/git2/indexer.h +27 -59
  80. data/vendor/libgit2/include/git2/inttypes.h +4 -0
  81. data/vendor/libgit2/include/git2/merge.h +13 -3
  82. data/vendor/libgit2/include/git2/message.h +14 -8
  83. data/vendor/libgit2/include/git2/net.h +9 -7
  84. data/vendor/libgit2/include/git2/notes.h +88 -29
  85. data/vendor/libgit2/include/git2/object.h +16 -6
  86. data/vendor/libgit2/include/git2/odb.h +80 -17
  87. data/vendor/libgit2/include/git2/odb_backend.h +47 -11
  88. data/vendor/libgit2/include/git2/oid.h +26 -17
  89. data/vendor/libgit2/include/git2/pack.h +62 -8
  90. data/vendor/libgit2/include/git2/push.h +131 -0
  91. data/vendor/libgit2/include/git2/refdb.h +103 -0
  92. data/vendor/libgit2/include/git2/refdb_backend.h +109 -0
  93. data/vendor/libgit2/include/git2/reflog.h +30 -21
  94. data/vendor/libgit2/include/git2/refs.h +215 -193
  95. data/vendor/libgit2/include/git2/refspec.h +22 -2
  96. data/vendor/libgit2/include/git2/remote.h +158 -37
  97. data/vendor/libgit2/include/git2/repository.h +150 -31
  98. data/vendor/libgit2/include/git2/reset.h +43 -9
  99. data/vendor/libgit2/include/git2/revparse.h +48 -4
  100. data/vendor/libgit2/include/git2/revwalk.h +25 -10
  101. data/vendor/libgit2/include/git2/signature.h +20 -12
  102. data/vendor/libgit2/include/git2/stash.h +121 -0
  103. data/vendor/libgit2/include/git2/status.h +122 -53
  104. data/vendor/libgit2/include/git2/strarray.h +17 -11
  105. data/vendor/libgit2/include/git2/submodule.h +42 -7
  106. data/vendor/libgit2/include/git2/tag.h +72 -59
  107. data/vendor/libgit2/include/git2/threads.h +4 -2
  108. data/vendor/libgit2/include/git2/trace.h +68 -0
  109. data/vendor/libgit2/include/git2/transport.h +328 -0
  110. data/vendor/libgit2/include/git2/tree.h +149 -120
  111. data/vendor/libgit2/include/git2/types.h +13 -12
  112. data/vendor/libgit2/include/git2/version.h +3 -3
  113. data/vendor/libgit2/src/amiga/map.c +2 -2
  114. data/vendor/libgit2/src/attr.c +58 -48
  115. data/vendor/libgit2/src/attr.h +4 -18
  116. data/vendor/libgit2/src/attr_file.c +30 -6
  117. data/vendor/libgit2/src/attr_file.h +6 -8
  118. data/vendor/libgit2/src/attrcache.h +24 -0
  119. data/vendor/libgit2/src/blob.c +30 -7
  120. data/vendor/libgit2/src/blob.h +1 -1
  121. data/vendor/libgit2/src/branch.c +361 -68
  122. data/vendor/libgit2/src/branch.h +17 -0
  123. data/vendor/libgit2/src/bswap.h +1 -1
  124. data/vendor/libgit2/src/buf_text.c +291 -0
  125. data/vendor/libgit2/src/buf_text.h +122 -0
  126. data/vendor/libgit2/src/buffer.c +27 -101
  127. data/vendor/libgit2/src/buffer.h +54 -39
  128. data/vendor/libgit2/src/cache.c +15 -6
  129. data/vendor/libgit2/src/cache.h +1 -1
  130. data/vendor/libgit2/src/cc-compat.h +3 -1
  131. data/vendor/libgit2/src/checkout.c +1165 -222
  132. data/vendor/libgit2/src/checkout.h +24 -0
  133. data/vendor/libgit2/src/clone.c +171 -86
  134. data/vendor/libgit2/src/commit.c +44 -45
  135. data/vendor/libgit2/src/commit.h +3 -3
  136. data/vendor/libgit2/src/commit_list.c +194 -0
  137. data/vendor/libgit2/src/commit_list.h +49 -0
  138. data/vendor/libgit2/src/common.h +44 -10
  139. data/vendor/libgit2/src/compress.c +1 -1
  140. data/vendor/libgit2/src/compress.h +1 -1
  141. data/vendor/libgit2/src/config.c +211 -124
  142. data/vendor/libgit2/src/config.h +23 -4
  143. data/vendor/libgit2/src/config_cache.c +2 -2
  144. data/vendor/libgit2/src/config_file.c +129 -53
  145. data/vendor/libgit2/src/config_file.h +10 -8
  146. data/vendor/libgit2/src/crlf.c +66 -67
  147. data/vendor/libgit2/src/date.c +12 -12
  148. data/vendor/libgit2/src/delta-apply.c +14 -1
  149. data/vendor/libgit2/src/delta-apply.h +18 -1
  150. data/vendor/libgit2/src/delta.c +40 -107
  151. data/vendor/libgit2/src/delta.h +19 -17
  152. data/vendor/libgit2/src/diff.c +347 -496
  153. data/vendor/libgit2/src/diff.h +27 -1
  154. data/vendor/libgit2/src/diff_output.c +564 -249
  155. data/vendor/libgit2/src/diff_output.h +15 -8
  156. data/vendor/libgit2/src/diff_tform.c +687 -0
  157. data/vendor/libgit2/src/errors.c +27 -36
  158. data/vendor/libgit2/src/fetch.c +13 -351
  159. data/vendor/libgit2/src/fetch.h +13 -3
  160. data/vendor/libgit2/src/fetchhead.c +295 -0
  161. data/vendor/libgit2/src/fetchhead.h +34 -0
  162. data/vendor/libgit2/src/filebuf.c +42 -15
  163. data/vendor/libgit2/src/filebuf.h +4 -2
  164. data/vendor/libgit2/src/fileops.c +466 -113
  165. data/vendor/libgit2/src/fileops.h +154 -28
  166. data/vendor/libgit2/src/filter.c +3 -75
  167. data/vendor/libgit2/src/filter.h +1 -29
  168. data/vendor/libgit2/src/fnmatch.c +1 -1
  169. data/vendor/libgit2/src/fnmatch.h +1 -1
  170. data/vendor/libgit2/src/global.c +54 -10
  171. data/vendor/libgit2/src/global.h +10 -1
  172. data/vendor/libgit2/src/graph.c +178 -0
  173. data/vendor/libgit2/src/hash.c +25 -52
  174. data/vendor/libgit2/src/hash.h +21 -9
  175. data/vendor/libgit2/src/{sha1/sha1.c → hash/hash_generic.c} +20 -12
  176. data/vendor/libgit2/src/hash/hash_generic.h +24 -0
  177. data/vendor/libgit2/src/hash/hash_openssl.h +45 -0
  178. data/vendor/libgit2/src/hash/hash_win32.c +291 -0
  179. data/vendor/libgit2/src/hash/hash_win32.h +140 -0
  180. data/vendor/libgit2/src/hashsig.c +368 -0
  181. data/vendor/libgit2/src/hashsig.h +72 -0
  182. data/vendor/libgit2/src/ignore.c +22 -15
  183. data/vendor/libgit2/src/ignore.h +6 -1
  184. data/vendor/libgit2/src/index.c +770 -171
  185. data/vendor/libgit2/src/index.h +13 -5
  186. data/vendor/libgit2/src/indexer.c +286 -431
  187. data/vendor/libgit2/src/iterator.c +854 -466
  188. data/vendor/libgit2/src/iterator.h +134 -109
  189. data/vendor/libgit2/src/map.h +1 -1
  190. data/vendor/libgit2/src/merge.c +296 -0
  191. data/vendor/libgit2/src/merge.h +22 -0
  192. data/vendor/libgit2/src/message.c +1 -1
  193. data/vendor/libgit2/src/message.h +1 -1
  194. data/vendor/libgit2/src/mwindow.c +35 -30
  195. data/vendor/libgit2/src/mwindow.h +2 -2
  196. data/vendor/libgit2/src/netops.c +162 -98
  197. data/vendor/libgit2/src/netops.h +50 -15
  198. data/vendor/libgit2/src/notes.c +109 -58
  199. data/vendor/libgit2/src/notes.h +2 -1
  200. data/vendor/libgit2/src/object.c +46 -57
  201. data/vendor/libgit2/src/object.h +1 -8
  202. data/vendor/libgit2/src/odb.c +151 -40
  203. data/vendor/libgit2/src/odb.h +5 -1
  204. data/vendor/libgit2/src/odb_loose.c +4 -5
  205. data/vendor/libgit2/src/odb_pack.c +122 -80
  206. data/vendor/libgit2/src/offmap.h +65 -0
  207. data/vendor/libgit2/src/oid.c +12 -4
  208. data/vendor/libgit2/src/oidmap.h +1 -1
  209. data/vendor/libgit2/src/pack-objects.c +88 -61
  210. data/vendor/libgit2/src/pack-objects.h +8 -8
  211. data/vendor/libgit2/src/pack.c +293 -28
  212. data/vendor/libgit2/src/pack.h +49 -4
  213. data/vendor/libgit2/src/path.c +103 -14
  214. data/vendor/libgit2/src/path.h +23 -7
  215. data/vendor/libgit2/src/pathspec.c +168 -0
  216. data/vendor/libgit2/src/pathspec.h +40 -0
  217. data/vendor/libgit2/src/pool.c +29 -4
  218. data/vendor/libgit2/src/pool.h +8 -1
  219. data/vendor/libgit2/src/posix.c +26 -27
  220. data/vendor/libgit2/src/posix.h +2 -3
  221. data/vendor/libgit2/src/pqueue.c +23 -1
  222. data/vendor/libgit2/src/pqueue.h +23 -1
  223. data/vendor/libgit2/src/push.c +653 -0
  224. data/vendor/libgit2/src/push.h +51 -0
  225. data/vendor/libgit2/src/refdb.c +185 -0
  226. data/vendor/libgit2/src/refdb.h +46 -0
  227. data/vendor/libgit2/src/refdb_fs.c +1024 -0
  228. data/vendor/libgit2/src/refdb_fs.h +15 -0
  229. data/vendor/libgit2/src/reflog.c +77 -45
  230. data/vendor/libgit2/src/reflog.h +1 -3
  231. data/vendor/libgit2/src/refs.c +366 -1326
  232. data/vendor/libgit2/src/refs.h +22 -13
  233. data/vendor/libgit2/src/refspec.c +46 -7
  234. data/vendor/libgit2/src/refspec.h +11 -1
  235. data/vendor/libgit2/src/remote.c +758 -120
  236. data/vendor/libgit2/src/remote.h +10 -5
  237. data/vendor/libgit2/src/repo_template.h +6 -6
  238. data/vendor/libgit2/src/repository.c +315 -96
  239. data/vendor/libgit2/src/repository.h +5 -3
  240. data/vendor/libgit2/src/reset.c +99 -81
  241. data/vendor/libgit2/src/revparse.c +157 -84
  242. data/vendor/libgit2/src/revwalk.c +68 -470
  243. data/vendor/libgit2/src/revwalk.h +44 -0
  244. data/vendor/libgit2/src/sha1_lookup.c +1 -1
  245. data/vendor/libgit2/src/sha1_lookup.h +1 -1
  246. data/vendor/libgit2/src/signature.c +68 -200
  247. data/vendor/libgit2/src/signature.h +1 -1
  248. data/vendor/libgit2/src/stash.c +663 -0
  249. data/vendor/libgit2/src/status.c +101 -79
  250. data/vendor/libgit2/src/strmap.h +1 -1
  251. data/vendor/libgit2/src/submodule.c +67 -51
  252. data/vendor/libgit2/src/submodule.h +1 -1
  253. data/vendor/libgit2/src/tag.c +35 -29
  254. data/vendor/libgit2/src/tag.h +1 -1
  255. data/vendor/libgit2/src/thread-utils.c +1 -1
  256. data/vendor/libgit2/src/thread-utils.h +2 -2
  257. data/vendor/libgit2/src/trace.c +39 -0
  258. data/vendor/libgit2/src/trace.h +56 -0
  259. data/vendor/libgit2/src/transport.c +81 -34
  260. data/vendor/libgit2/src/transports/cred.c +60 -0
  261. data/vendor/libgit2/src/transports/cred_helpers.c +49 -0
  262. data/vendor/libgit2/src/transports/git.c +234 -127
  263. data/vendor/libgit2/src/transports/http.c +761 -433
  264. data/vendor/libgit2/src/transports/local.c +460 -64
  265. data/vendor/libgit2/src/transports/smart.c +345 -0
  266. data/vendor/libgit2/src/transports/smart.h +179 -0
  267. data/vendor/libgit2/src/{pkt.c → transports/smart_pkt.c} +131 -12
  268. data/vendor/libgit2/src/transports/smart_protocol.c +856 -0
  269. data/vendor/libgit2/src/transports/winhttp.c +1136 -0
  270. data/vendor/libgit2/src/tree-cache.c +2 -2
  271. data/vendor/libgit2/src/tree-cache.h +1 -1
  272. data/vendor/libgit2/src/tree.c +239 -166
  273. data/vendor/libgit2/src/tree.h +11 -2
  274. data/vendor/libgit2/src/tsort.c +39 -23
  275. data/vendor/libgit2/src/unix/map.c +1 -1
  276. data/vendor/libgit2/src/unix/posix.h +12 -2
  277. data/vendor/libgit2/src/unix/realpath.c +30 -0
  278. data/vendor/libgit2/src/util.c +250 -13
  279. data/vendor/libgit2/src/util.h +71 -14
  280. data/vendor/libgit2/src/vector.c +123 -60
  281. data/vendor/libgit2/src/vector.h +24 -22
  282. data/vendor/libgit2/src/win32/dir.c +1 -1
  283. data/vendor/libgit2/src/win32/dir.h +1 -1
  284. data/vendor/libgit2/src/win32/error.c +77 -0
  285. data/vendor/libgit2/src/win32/error.h +13 -0
  286. data/vendor/libgit2/src/win32/findfile.c +143 -54
  287. data/vendor/libgit2/src/win32/findfile.h +10 -6
  288. data/vendor/libgit2/src/win32/map.c +1 -1
  289. data/vendor/libgit2/src/win32/mingw-compat.h +1 -1
  290. data/vendor/libgit2/src/win32/msvc-compat.h +10 -1
  291. data/vendor/libgit2/src/win32/posix.h +10 -1
  292. data/vendor/libgit2/src/win32/posix_w32.c +132 -63
  293. data/vendor/libgit2/src/win32/precompiled.c +1 -1
  294. data/vendor/libgit2/src/win32/pthread.c +1 -1
  295. data/vendor/libgit2/src/win32/pthread.h +1 -1
  296. data/vendor/libgit2/src/win32/utf-conv.c +5 -5
  297. data/vendor/libgit2/src/win32/utf-conv.h +3 -3
  298. data/vendor/libgit2/src/win32/version.h +20 -0
  299. metadata +308 -252
  300. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  301. data/test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 +0 -0
  302. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  303. data/test/index_test.rb~ +0 -218
  304. data/vendor/libgit2/src/pkt.h +0 -91
  305. data/vendor/libgit2/src/ppc/sha1.c +0 -70
  306. data/vendor/libgit2/src/ppc/sha1.h +0 -26
  307. data/vendor/libgit2/src/protocol.c +0 -110
  308. data/vendor/libgit2/src/protocol.h +0 -21
  309. data/vendor/libgit2/src/sha1.h +0 -33
  310. data/vendor/libgit2/src/transport.h +0 -148
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2009-2012 the libgit2 contributors
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
3
  *
4
4
  * This file is part of libgit2, distributed under the GNU GPL v2 with
5
5
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,299 +10,565 @@
10
10
  #include "ignore.h"
11
11
  #include "buffer.h"
12
12
  #include "git2/submodule.h"
13
+ #include <ctype.h>
14
+
15
+ #define ITERATOR_SET_CB(P,NAME_LC) do { \
16
+ (P)->cb.current = NAME_LC ## _iterator__current; \
17
+ (P)->cb.advance = NAME_LC ## _iterator__advance; \
18
+ (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
19
+ (P)->cb.seek = NAME_LC ## _iterator__seek; \
20
+ (P)->cb.reset = NAME_LC ## _iterator__reset; \
21
+ (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
22
+ (P)->cb.free = NAME_LC ## _iterator__free; \
23
+ } while (0)
24
+
25
+ #define ITERATOR_CASE_FLAGS \
26
+ (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
13
27
 
14
- #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
28
+ #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
15
29
  (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
16
30
  GITERR_CHECK_ALLOC(P); \
17
- (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \
31
+ (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
32
+ (P)->base.cb = &(P)->cb; \
33
+ ITERATOR_SET_CB(P,NAME_LC); \
34
+ (P)->base.repo = (REPO); \
18
35
  (P)->base.start = start ? git__strdup(start) : NULL; \
19
36
  (P)->base.end = end ? git__strdup(end) : NULL; \
20
- (P)->base.ignore_case = 0; \
21
- (P)->base.current = NAME_LC ## _iterator__current; \
22
- (P)->base.at_end = NAME_LC ## _iterator__at_end; \
23
- (P)->base.advance = NAME_LC ## _iterator__advance; \
24
- (P)->base.seek = NAME_LC ## _iterator__seek; \
25
- (P)->base.reset = NAME_LC ## _iterator__reset; \
26
- (P)->base.free = NAME_LC ## _iterator__free; \
27
- if ((start && !(P)->base.start) || (end && !(P)->base.end)) \
28
- return -1; \
37
+ if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
38
+ git__free(P); return -1; } \
39
+ (P)->base.prefixcomp = git__prefixcmp; \
40
+ (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
41
+ if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
42
+ (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
29
43
  } while (0)
30
44
 
45
+ #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
46
+ #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
47
+ #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
48
+ #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
49
+ #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
50
+
51
+ #define iterator__end(I) ((git_iterator *)(I))->end
52
+ #define iterator__past_end(I,PATH) \
53
+ (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
54
+
31
55
 
32
- static int empty_iterator__no_item(
33
- git_iterator *iter, const git_index_entry **entry)
56
+ static int iterator__reset_range(
57
+ git_iterator *iter, const char *start, const char *end)
34
58
  {
35
- GIT_UNUSED(iter);
36
- *entry = NULL;
59
+ if (start) {
60
+ if (iter->start)
61
+ git__free(iter->start);
62
+ iter->start = git__strdup(start);
63
+ GITERR_CHECK_ALLOC(iter->start);
64
+ }
65
+
66
+ if (end) {
67
+ if (iter->end)
68
+ git__free(iter->end);
69
+ iter->end = git__strdup(end);
70
+ GITERR_CHECK_ALLOC(iter->end);
71
+ }
72
+
37
73
  return 0;
38
74
  }
39
75
 
40
- static int empty_iterator__at_end(git_iterator *iter)
76
+ static int iterator__update_ignore_case(
77
+ git_iterator *iter,
78
+ git_iterator_flag_t flags)
41
79
  {
42
- GIT_UNUSED(iter);
43
- return 1;
80
+ int error = 0, ignore_case = -1;
81
+
82
+ if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
83
+ ignore_case = true;
84
+ else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
85
+ ignore_case = false;
86
+ else {
87
+ git_index *index;
88
+
89
+ if (!(error = git_repository_index__weakptr(&index, iter->repo)))
90
+ ignore_case = (index->ignore_case != false);
91
+ }
92
+
93
+ if (ignore_case > 0)
94
+ iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
95
+ else if (ignore_case == 0)
96
+ iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
97
+
98
+ iter->prefixcomp = iterator__ignore_case(iter) ?
99
+ git__prefixcmp_icase : git__prefixcmp;
100
+
101
+ return error;
44
102
  }
45
103
 
46
- static int empty_iterator__noop(git_iterator *iter)
104
+ GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
47
105
  {
48
- GIT_UNUSED(iter);
106
+ if (entry) *entry = NULL;
107
+ }
108
+
109
+
110
+ static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
111
+ {
112
+ GIT_UNUSED(i);
113
+ iterator__clear_entry(e);
49
114
  return 0;
50
115
  }
51
116
 
52
- static int empty_iterator__seek(git_iterator *iter, const char *prefix)
117
+ static int empty_iterator__seek(git_iterator *i, const char *p)
53
118
  {
54
- GIT_UNUSED(iter);
55
- GIT_UNUSED(prefix);
119
+ GIT_UNUSED(i); GIT_UNUSED(p);
56
120
  return -1;
57
121
  }
58
122
 
59
- static void empty_iterator__free(git_iterator *iter)
123
+ static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
60
124
  {
61
- GIT_UNUSED(iter);
125
+ GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
126
+ return 0;
62
127
  }
63
128
 
64
- int git_iterator_for_nothing(git_iterator **iter)
129
+ static int empty_iterator__at_end(git_iterator *i)
65
130
  {
66
- git_iterator *i = git__calloc(1, sizeof(git_iterator));
67
- GITERR_CHECK_ALLOC(i);
131
+ GIT_UNUSED(i);
132
+ return 1;
133
+ }
68
134
 
69
- i->type = GIT_ITERATOR_EMPTY;
70
- i->current = empty_iterator__no_item;
71
- i->at_end = empty_iterator__at_end;
72
- i->advance = empty_iterator__no_item;
73
- i->seek = empty_iterator__seek;
74
- i->reset = empty_iterator__noop;
75
- i->free = empty_iterator__free;
135
+ static void empty_iterator__free(git_iterator *i)
136
+ {
137
+ GIT_UNUSED(i);
138
+ }
76
139
 
77
- *iter = i;
140
+ typedef struct {
141
+ git_iterator base;
142
+ git_iterator_callbacks cb;
143
+ } empty_iterator;
78
144
 
145
+ int git_iterator_for_nothing(
146
+ git_iterator **iter,
147
+ git_iterator_flag_t flags,
148
+ const char *start,
149
+ const char *end)
150
+ {
151
+ empty_iterator *i;
152
+
153
+ #define empty_iterator__current empty_iterator__noop
154
+ #define empty_iterator__advance empty_iterator__noop
155
+ #define empty_iterator__advance_into empty_iterator__noop
156
+
157
+ ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
158
+
159
+ if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
160
+ i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
161
+
162
+ *iter = (git_iterator *)i;
79
163
  return 0;
80
164
  }
81
165
 
82
166
 
167
+ typedef struct tree_iterator_entry tree_iterator_entry;
168
+ struct tree_iterator_entry {
169
+ tree_iterator_entry *parent;
170
+ const git_tree_entry *te;
171
+ git_tree *tree;
172
+ };
173
+
83
174
  typedef struct tree_iterator_frame tree_iterator_frame;
84
175
  struct tree_iterator_frame {
85
- tree_iterator_frame *next, *prev;
86
- git_tree *tree;
87
- char *start;
88
- unsigned int index;
176
+ tree_iterator_frame *up, *down;
177
+
178
+ size_t n_entries; /* items in this frame */
179
+ size_t current; /* start of currently active range in frame */
180
+ size_t next; /* start of next range in frame */
181
+
182
+ const char *start;
183
+ size_t startlen;
184
+
185
+ tree_iterator_entry *entries[GIT_FLEX_ARRAY];
89
186
  };
90
187
 
91
188
  typedef struct {
92
189
  git_iterator base;
93
- git_repository *repo;
94
- tree_iterator_frame *stack, *tail;
190
+ git_iterator_callbacks cb;
191
+ tree_iterator_frame *head, *root;
192
+ git_pool pool;
95
193
  git_index_entry entry;
96
194
  git_buf path;
195
+ int path_ambiguities;
97
196
  bool path_has_filename;
197
+ int (*strncomp)(const char *a, const char *b, size_t sz);
98
198
  } tree_iterator;
99
199
 
100
- static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
101
- {
102
- return (ti->stack == NULL) ? NULL :
103
- git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
104
- }
105
-
106
200
  static char *tree_iterator__current_filename(
107
201
  tree_iterator *ti, const git_tree_entry *te)
108
202
  {
109
203
  if (!ti->path_has_filename) {
110
204
  if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
111
205
  return NULL;
206
+
207
+ if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
208
+ return NULL;
209
+
112
210
  ti->path_has_filename = true;
113
211
  }
114
212
 
115
213
  return ti->path.ptr;
116
214
  }
117
215
 
118
- static void tree_iterator__pop_frame(tree_iterator *ti)
216
+ static void tree_iterator__rewrite_filename(tree_iterator *ti)
119
217
  {
120
- tree_iterator_frame *tf = ti->stack;
121
- ti->stack = tf->next;
122
- if (ti->stack != NULL) {
123
- git_tree_free(tf->tree); /* don't free the initial tree */
124
- ti->stack->prev = NULL; /* disconnect prev */
218
+ tree_iterator_entry *scan = ti->head->entries[ti->head->current];
219
+ ssize_t strpos = ti->path.size;
220
+ const git_tree_entry *te;
221
+
222
+ if (strpos && ti->path.ptr[strpos - 1] == '/')
223
+ strpos--;
224
+
225
+ for (; scan && (te = scan->te); scan = scan->parent) {
226
+ strpos -= te->filename_len;
227
+ memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
228
+ strpos -= 1; /* separator */
125
229
  }
126
- git__free(tf);
127
230
  }
128
231
 
129
- static int tree_iterator__to_end(tree_iterator *ti)
232
+ static int tree_iterator__te_cmp(
233
+ const git_tree_entry *a,
234
+ const git_tree_entry *b,
235
+ int (*compare)(const char *, const char *, size_t))
236
+ {
237
+ return git_path_cmp(
238
+ a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
239
+ b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
240
+ compare);
241
+ }
242
+
243
+ static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
244
+ {
245
+ const tree_iterator_entry *ae = a, *be = b;
246
+ int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
247
+
248
+ if (!cmp) {
249
+ /* stabilize sort order among equivalent names */
250
+ if (!ae->parent->te || !be->parent->te)
251
+ cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
252
+ else
253
+ cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
254
+ }
255
+
256
+ return cmp;
257
+ }
258
+
259
+ static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
260
+ {
261
+ const tree_iterator_frame *tf = key;
262
+ const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
263
+
264
+ return git_path_cmp(
265
+ tf->start, tf->startlen, false,
266
+ te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
267
+ ((tree_iterator *)p)->strncomp);
268
+ }
269
+
270
+ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
130
271
  {
131
- while (ti->stack && ti->stack->next)
132
- tree_iterator__pop_frame(ti);
272
+ int error;
273
+ const git_tree_entry *te, *last = NULL;
274
+
275
+ tf->next = tf->current;
133
276
 
134
- if (ti->stack)
135
- ti->stack->index = git_tree_entrycount(ti->stack->tree);
277
+ for (; tf->next < tf->n_entries; tf->next++, last = te) {
278
+ te = tf->entries[tf->next]->te;
279
+
280
+ if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
281
+ break;
282
+
283
+ /* load trees for items in [current,next) range */
284
+ if (git_tree_entry__is_tree(te) &&
285
+ (error = git_tree_lookup(
286
+ &tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0)
287
+ return error;
288
+ }
289
+
290
+ if (tf->next > tf->current + 1)
291
+ ti->path_ambiguities++;
292
+
293
+ if (last && !tree_iterator__current_filename(ti, last))
294
+ return -1;
136
295
 
137
296
  return 0;
138
297
  }
139
298
 
140
- static int tree_iterator__current(
141
- git_iterator *self, const git_index_entry **entry)
299
+ GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
142
300
  {
143
- tree_iterator *ti = (tree_iterator *)self;
144
- const git_tree_entry *te = tree_iterator__tree_entry(ti);
301
+ return (ti->head->current < ti->head->n_entries &&
302
+ ti->head->entries[ti->head->current]->tree != NULL);
303
+ }
145
304
 
146
- if (entry)
147
- *entry = NULL;
305
+ static int tree_iterator__push_frame(tree_iterator *ti)
306
+ {
307
+ int error = 0;
308
+ tree_iterator_frame *head = ti->head, *tf = NULL;
309
+ size_t i, n_entries = 0;
148
310
 
149
- if (te == NULL)
311
+ if (head->current >= head->n_entries || !head->entries[head->current]->tree)
150
312
  return 0;
151
313
 
152
- ti->entry.mode = te->attr;
153
- git_oid_cpy(&ti->entry.oid, &te->oid);
314
+ for (i = head->current; i < head->next; ++i)
315
+ n_entries += git_tree_entrycount(head->entries[i]->tree);
154
316
 
155
- ti->entry.path = tree_iterator__current_filename(ti, te);
156
- if (ti->entry.path == NULL)
157
- return -1;
317
+ tf = git__calloc(sizeof(tree_iterator_frame) +
318
+ n_entries * sizeof(tree_iterator_entry *), 1);
319
+ GITERR_CHECK_ALLOC(tf);
158
320
 
159
- if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
160
- return tree_iterator__to_end(ti);
321
+ tf->n_entries = n_entries;
161
322
 
162
- if (entry)
163
- *entry = &ti->entry;
323
+ tf->up = head;
324
+ head->down = tf;
325
+ ti->head = tf;
326
+
327
+ for (i = head->current, n_entries = 0; i < head->next; ++i) {
328
+ git_tree *tree = head->entries[i]->tree;
329
+ size_t j, max_j = git_tree_entrycount(tree);
330
+
331
+ for (j = 0; j < max_j; ++j) {
332
+ tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
333
+ GITERR_CHECK_ALLOC(entry);
334
+
335
+ entry->parent = head->entries[i];
336
+ entry->te = git_tree_entry_byindex(tree, j);
337
+ entry->tree = NULL;
338
+
339
+ tf->entries[n_entries++] = entry;
340
+ }
341
+ }
342
+
343
+ /* if ignore_case, sort entries case insensitively */
344
+ if (iterator__ignore_case(ti))
345
+ git__tsort_r(
346
+ (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
347
+
348
+ /* pick tf->current based on "start" (or start at zero) */
349
+ if (head->startlen > 0) {
350
+ git__bsearch_r((void **)tf->entries, tf->n_entries, head,
351
+ tree_iterator__search_cmp, ti, &tf->current);
352
+
353
+ while (tf->current &&
354
+ !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
355
+ tf->current--;
356
+
357
+ if ((tf->start = strchr(head->start, '/')) != NULL) {
358
+ tf->start++;
359
+ tf->startlen = strlen(tf->start);
360
+ }
361
+ }
362
+
363
+ ti->path_has_filename = false;
364
+
365
+ if ((error = tree_iterator__set_next(ti, tf)) < 0)
366
+ return error;
367
+
368
+ /* autoexpand as needed */
369
+ if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
370
+ return tree_iterator__push_frame(ti);
164
371
 
165
372
  return 0;
166
373
  }
167
374
 
168
- static int tree_iterator__at_end(git_iterator *self)
375
+ static bool tree_iterator__move_to_next(
376
+ tree_iterator *ti, tree_iterator_frame *tf)
169
377
  {
170
- return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
378
+ if (tf->next > tf->current + 1)
379
+ ti->path_ambiguities--;
380
+
381
+ if (!tf->up) { /* at root */
382
+ tf->current = tf->next;
383
+ return false;
384
+ }
385
+
386
+ for (; tf->current < tf->next; tf->current++) {
387
+ git_tree_free(tf->entries[tf->current]->tree);
388
+ tf->entries[tf->current]->tree = NULL;
389
+ }
390
+
391
+ return (tf->current < tf->n_entries);
171
392
  }
172
393
 
173
- static tree_iterator_frame *tree_iterator__alloc_frame(
174
- git_tree *tree, char *start)
394
+ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
175
395
  {
176
- tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
177
- if (!tf)
178
- return NULL;
396
+ tree_iterator_frame *tf = ti->head;
179
397
 
180
- tf->tree = tree;
398
+ if (!tf->up)
399
+ return false;
181
400
 
182
- if (start && *start) {
183
- tf->start = start;
184
- tf->index = git_tree__prefix_position(tree, start);
401
+ ti->head = tf->up;
402
+ ti->head->down = NULL;
403
+
404
+ tree_iterator__move_to_next(ti, tf);
405
+
406
+ if (!final) { /* if final, don't bother to clean up */
407
+ git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
408
+ git_buf_rtruncate_at_char(&ti->path, '/');
185
409
  }
186
410
 
187
- return tf;
411
+ git__free(tf);
412
+
413
+ return true;
188
414
  }
189
415
 
190
- static int tree_iterator__expand_tree(tree_iterator *ti)
416
+ static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
191
417
  {
192
- int error;
193
- git_tree *subtree;
194
- const git_tree_entry *te = tree_iterator__tree_entry(ti);
195
- tree_iterator_frame *tf;
196
- char *relpath;
418
+ while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
197
419
 
198
- while (te != NULL && git_tree_entry__is_tree(te)) {
199
- if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
200
- return -1;
420
+ if (!final) {
421
+ ti->head->current = to_end ? ti->head->n_entries : 0;
422
+ ti->path_ambiguities = 0;
423
+ git_buf_clear(&ti->path);
424
+ }
201
425
 
202
- /* check that we have not passed the range end */
203
- if (ti->base.end != NULL &&
204
- git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
205
- return tree_iterator__to_end(ti);
426
+ return 0;
427
+ }
206
428
 
207
- if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
208
- return error;
429
+ static int tree_iterator__current(
430
+ const git_index_entry **entry, git_iterator *self)
431
+ {
432
+ tree_iterator *ti = (tree_iterator *)self;
433
+ tree_iterator_frame *tf = ti->head;
434
+ const git_tree_entry *te;
209
435
 
210
- relpath = NULL;
436
+ iterator__clear_entry(entry);
211
437
 
212
- /* apply range start to new frame if relevant */
213
- if (ti->stack->start &&
214
- git__prefixcmp(ti->stack->start, te->filename) == 0)
215
- {
216
- size_t namelen = strlen(te->filename);
217
- if (ti->stack->start[namelen] == '/')
218
- relpath = ti->stack->start + namelen + 1;
219
- }
438
+ if (tf->current >= tf->n_entries)
439
+ return 0;
440
+ te = tf->entries[tf->current]->te;
220
441
 
221
- if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
222
- return -1;
442
+ ti->entry.mode = te->attr;
443
+ git_oid_cpy(&ti->entry.oid, &te->oid);
223
444
 
224
- tf->next = ti->stack;
225
- ti->stack = tf;
226
- tf->next->prev = tf;
445
+ ti->entry.path = tree_iterator__current_filename(ti, te);
446
+ GITERR_CHECK_ALLOC(ti->entry.path);
227
447
 
228
- te = tree_iterator__tree_entry(ti);
229
- }
448
+ if (ti->path_ambiguities > 0)
449
+ tree_iterator__rewrite_filename(ti);
450
+
451
+ if (iterator__past_end(ti, ti->entry.path))
452
+ return tree_iterator__pop_all(ti, true, false);
453
+
454
+ if (entry)
455
+ *entry = &ti->entry;
230
456
 
231
457
  return 0;
232
458
  }
233
459
 
234
- static int tree_iterator__advance(
235
- git_iterator *self, const git_index_entry **entry)
460
+ static int tree_iterator__advance_into(
461
+ const git_index_entry **entry, git_iterator *self)
236
462
  {
237
463
  int error = 0;
238
464
  tree_iterator *ti = (tree_iterator *)self;
239
- const git_tree_entry *te = NULL;
240
465
 
241
- if (entry != NULL)
242
- *entry = NULL;
466
+ iterator__clear_entry(entry);
243
467
 
244
- if (ti->path_has_filename) {
245
- git_buf_rtruncate_at_char(&ti->path, '/');
246
- ti->path_has_filename = false;
247
- }
468
+ if (tree_iterator__at_tree(ti) &&
469
+ !(error = tree_iterator__push_frame(ti)))
470
+ error = tree_iterator__current(entry, self);
248
471
 
249
- while (ti->stack != NULL) {
250
- te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
251
- if (te != NULL)
252
- break;
472
+ return error;
473
+ }
474
+
475
+ static int tree_iterator__advance(
476
+ const git_index_entry **entry, git_iterator *self)
477
+ {
478
+ int error;
479
+ tree_iterator *ti = (tree_iterator *)self;
480
+ tree_iterator_frame *tf = ti->head;
481
+
482
+ iterator__clear_entry(entry);
483
+
484
+ if (tf->current > tf->n_entries)
485
+ return 0;
253
486
 
254
- tree_iterator__pop_frame(ti);
487
+ if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
488
+ tree_iterator__at_tree(ti))
489
+ return tree_iterator__advance_into(entry, self);
255
490
 
491
+ if (ti->path_has_filename) {
256
492
  git_buf_rtruncate_at_char(&ti->path, '/');
493
+ ti->path_has_filename = false;
257
494
  }
258
495
 
259
- if (te && git_tree_entry__is_tree(te))
260
- error = tree_iterator__expand_tree(ti);
496
+ /* scan forward and up, advancing in frame or popping frame when done */
497
+ while (!tree_iterator__move_to_next(ti, tf) &&
498
+ tree_iterator__pop_frame(ti, false))
499
+ tf = ti->head;
261
500
 
262
- if (!error)
263
- error = tree_iterator__current(self, entry);
501
+ /* find next and load trees */
502
+ if ((error = tree_iterator__set_next(ti, tf)) < 0)
503
+ return error;
264
504
 
265
- return error;
505
+ /* deal with include_trees / auto_expand as needed */
506
+ if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
507
+ return tree_iterator__advance_into(entry, self);
508
+
509
+ return tree_iterator__current(entry, self);
266
510
  }
267
511
 
268
512
  static int tree_iterator__seek(git_iterator *self, const char *prefix)
269
513
  {
270
- GIT_UNUSED(self);
271
- GIT_UNUSED(prefix);
272
- /* pop stack until matches prefix */
273
- /* seek item in current frame matching prefix */
274
- /* push stack which matches prefix */
514
+ GIT_UNUSED(self); GIT_UNUSED(prefix);
275
515
  return -1;
276
516
  }
277
517
 
278
- static void tree_iterator__free(git_iterator *self)
518
+ static int tree_iterator__reset(
519
+ git_iterator *self, const char *start, const char *end)
279
520
  {
280
521
  tree_iterator *ti = (tree_iterator *)self;
281
- while (ti->stack != NULL)
282
- tree_iterator__pop_frame(ti);
283
- git_buf_free(&ti->path);
522
+
523
+ tree_iterator__pop_all(ti, false, false);
524
+
525
+ if (iterator__reset_range(self, start, end) < 0)
526
+ return -1;
527
+
528
+ return tree_iterator__push_frame(ti); /* re-expand root tree */
284
529
  }
285
530
 
286
- static int tree_iterator__reset(git_iterator *self)
531
+ static int tree_iterator__at_end(git_iterator *self)
287
532
  {
288
533
  tree_iterator *ti = (tree_iterator *)self;
534
+ return (ti->head->current >= ti->head->n_entries);
535
+ }
289
536
 
290
- while (ti->stack && ti->stack->next)
291
- tree_iterator__pop_frame(ti);
537
+ static void tree_iterator__free(git_iterator *self)
538
+ {
539
+ tree_iterator *ti = (tree_iterator *)self;
292
540
 
293
- if (ti->stack)
294
- ti->stack->index =
295
- git_tree__prefix_position(ti->stack->tree, ti->base.start);
541
+ tree_iterator__pop_all(ti, true, false);
296
542
 
297
- git_buf_clear(&ti->path);
543
+ git_tree_free(ti->head->entries[0]->tree);
544
+ git__free(ti->head);
545
+ git_pool_clear(&ti->pool);
546
+ git_buf_free(&ti->path);
547
+ }
298
548
 
299
- return tree_iterator__expand_tree(ti);
549
+ static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
550
+ {
551
+ size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
552
+ tree_iterator_frame *root = git__calloc(sz, sizeof(char));
553
+ GITERR_CHECK_ALLOC(root);
554
+
555
+ root->n_entries = 1;
556
+ root->next = 1;
557
+ root->start = ti->base.start;
558
+ root->startlen = root->start ? strlen(root->start) : 0;
559
+ root->entries[0] = git_pool_mallocz(&ti->pool, 1);
560
+ GITERR_CHECK_ALLOC(root->entries[0]);
561
+ root->entries[0]->tree = tree;
562
+
563
+ ti->head = ti->root = root;
564
+
565
+ return 0;
300
566
  }
301
567
 
302
- int git_iterator_for_tree_range(
568
+ int git_iterator_for_tree(
303
569
  git_iterator **iter,
304
- git_repository *repo,
305
570
  git_tree *tree,
571
+ git_iterator_flag_t flags,
306
572
  const char *start,
307
573
  const char *end)
308
574
  {
@@ -310,42 +576,124 @@ int git_iterator_for_tree_range(
310
576
  tree_iterator *ti;
311
577
 
312
578
  if (tree == NULL)
313
- return git_iterator_for_nothing(iter);
579
+ return git_iterator_for_nothing(iter, flags, start, end);
314
580
 
315
- ITERATOR_BASE_INIT(ti, tree, TREE);
581
+ if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
582
+ return error;
316
583
 
317
- ti->repo = repo;
318
- ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start);
584
+ ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
319
585
 
320
- if ((error = tree_iterator__expand_tree(ti)) < 0)
321
- git_iterator_free((git_iterator *)ti);
322
- else
323
- *iter = (git_iterator *)ti;
586
+ if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
587
+ goto fail;
588
+ ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
324
589
 
590
+ if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
591
+ (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
592
+ (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
593
+ goto fail;
594
+
595
+ *iter = (git_iterator *)ti;
596
+ return 0;
597
+
598
+ fail:
599
+ git_iterator_free((git_iterator *)ti);
325
600
  return error;
326
601
  }
327
602
 
328
603
 
329
604
  typedef struct {
330
605
  git_iterator base;
606
+ git_iterator_callbacks cb;
331
607
  git_index *index;
332
- unsigned int current;
608
+ size_t current;
609
+ /* when not in autoexpand mode, use these to represent "tree" state */
610
+ git_buf partial;
611
+ size_t partial_pos;
612
+ char restore_terminator;
613
+ git_index_entry tree_entry;
333
614
  } index_iterator;
334
615
 
335
- static int index_iterator__current(
336
- git_iterator *self, const git_index_entry **entry)
616
+ static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
337
617
  {
338
- index_iterator *ii = (index_iterator *)self;
339
- git_index_entry *ie = git_index_get(ii->index, ii->current);
618
+ const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
340
619
 
341
- if (ie != NULL &&
342
- ii->base.end != NULL &&
343
- ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)
344
- {
620
+ if (ie != NULL && iterator__past_end(ii, ie->path)) {
345
621
  ii->current = git_index_entrycount(ii->index);
346
622
  ie = NULL;
347
623
  }
348
624
 
625
+ return ie;
626
+ }
627
+
628
+ static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
629
+ {
630
+ const git_index_entry *ie;
631
+
632
+ while ((ie = index_iterator__index_entry(ii)) != NULL &&
633
+ git_index_entry_stage(ie) != 0)
634
+ ii->current++;
635
+
636
+ return ie;
637
+ }
638
+
639
+ static void index_iterator__next_prefix_tree(index_iterator *ii)
640
+ {
641
+ const char *slash;
642
+
643
+ if (!iterator__include_trees(ii))
644
+ return;
645
+
646
+ slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
647
+
648
+ if (slash != NULL) {
649
+ ii->partial_pos = (slash - ii->partial.ptr) + 1;
650
+ ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
651
+ ii->partial.ptr[ii->partial_pos] = '\0';
652
+ } else {
653
+ ii->partial_pos = ii->partial.size;
654
+ }
655
+
656
+ if (index_iterator__index_entry(ii) == NULL)
657
+ ii->partial_pos = ii->partial.size;
658
+ }
659
+
660
+ static int index_iterator__first_prefix_tree(index_iterator *ii)
661
+ {
662
+ const git_index_entry *ie = index_iterator__skip_conflicts(ii);
663
+ const char *scan, *prior, *slash;
664
+
665
+ if (!ie || !iterator__include_trees(ii))
666
+ return 0;
667
+
668
+ /* find longest common prefix with prior index entry */
669
+ for (scan = slash = ie->path, prior = ii->partial.ptr;
670
+ *scan && *scan == *prior; ++scan, ++prior)
671
+ if (*scan == '/')
672
+ slash = scan;
673
+
674
+ if (git_buf_sets(&ii->partial, ie->path) < 0)
675
+ return -1;
676
+
677
+ ii->partial_pos = (slash - ie->path) + 1;
678
+ index_iterator__next_prefix_tree(ii);
679
+
680
+ return 0;
681
+ }
682
+
683
+ #define index_iterator__at_tree(I) \
684
+ (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
685
+
686
+ static int index_iterator__current(
687
+ const git_index_entry **entry, git_iterator *self)
688
+ {
689
+ index_iterator *ii = (index_iterator *)self;
690
+ const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
691
+
692
+ if (ie != NULL && index_iterator__at_tree(ii)) {
693
+ ii->tree_entry.path = ii->partial.ptr;
694
+ ie = &ii->tree_entry;
695
+ }
696
+
349
697
  if (entry)
350
698
  *entry = ie;
351
699
 
@@ -359,28 +707,90 @@ static int index_iterator__at_end(git_iterator *self)
359
707
  }
360
708
 
361
709
  static int index_iterator__advance(
362
- git_iterator *self, const git_index_entry **entry)
710
+ const git_index_entry **entry, git_iterator *self)
363
711
  {
364
712
  index_iterator *ii = (index_iterator *)self;
713
+ size_t entrycount = git_index_entrycount(ii->index);
714
+ const git_index_entry *ie;
715
+
716
+ if (index_iterator__at_tree(ii)) {
717
+ if (iterator__do_autoexpand(ii)) {
718
+ ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
719
+ index_iterator__next_prefix_tree(ii);
720
+ } else {
721
+ /* advance to sibling tree (i.e. find entry with new prefix) */
722
+ while (ii->current < entrycount) {
723
+ ii->current++;
724
+
725
+ if (!(ie = git_index_get_byindex(ii->index, ii->current)) ||
726
+ ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
727
+ break;
728
+ }
729
+
730
+ if (index_iterator__first_prefix_tree(ii) < 0)
731
+ return -1;
732
+ }
733
+ } else {
734
+ if (ii->current < entrycount)
735
+ ii->current++;
365
736
 
366
- if (ii->current < git_index_entrycount(ii->index))
367
- ii->current++;
737
+ if (index_iterator__first_prefix_tree(ii) < 0)
738
+ return -1;
739
+ }
740
+
741
+ return index_iterator__current(entry, self);
742
+ }
743
+
744
+ static int index_iterator__advance_into(
745
+ const git_index_entry **entry, git_iterator *self)
746
+ {
747
+ index_iterator *ii = (index_iterator *)self;
748
+ const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
749
+
750
+ if (ie != NULL && index_iterator__at_tree(ii)) {
751
+ if (ii->restore_terminator)
752
+ ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
753
+ index_iterator__next_prefix_tree(ii);
754
+ }
368
755
 
369
- return index_iterator__current(self, entry);
756
+ return index_iterator__current(entry, self);
370
757
  }
371
758
 
372
759
  static int index_iterator__seek(git_iterator *self, const char *prefix)
373
760
  {
374
- GIT_UNUSED(self);
375
- GIT_UNUSED(prefix);
376
- /* find last item before prefix */
761
+ GIT_UNUSED(self); GIT_UNUSED(prefix);
377
762
  return -1;
378
763
  }
379
764
 
380
- static int index_iterator__reset(git_iterator *self)
765
+ static int index_iterator__reset(
766
+ git_iterator *self, const char *start, const char *end)
381
767
  {
382
768
  index_iterator *ii = (index_iterator *)self;
383
- ii->current = 0;
769
+ const git_index_entry *ie;
770
+
771
+ if (iterator__reset_range(self, start, end) < 0)
772
+ return -1;
773
+
774
+ ii->current = ii->base.start ?
775
+ git_index__prefix_position(ii->index, ii->base.start) : 0;
776
+
777
+ if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
778
+ return 0;
779
+
780
+ if (git_buf_sets(&ii->partial, ie->path) < 0)
781
+ return -1;
782
+
783
+ ii->partial_pos = 0;
784
+
785
+ if (ii->base.start) {
786
+ size_t startlen = strlen(ii->base.start);
787
+
788
+ ii->partial_pos = (startlen > ii->partial.size) ?
789
+ ii->partial.size : startlen;
790
+ }
791
+
792
+ index_iterator__next_prefix_tree(ii);
793
+
384
794
  return 0;
385
795
  }
386
796
 
@@ -389,77 +799,98 @@ static void index_iterator__free(git_iterator *self)
389
799
  index_iterator *ii = (index_iterator *)self;
390
800
  git_index_free(ii->index);
391
801
  ii->index = NULL;
802
+
803
+ git_buf_free(&ii->partial);
392
804
  }
393
805
 
394
- int git_iterator_for_index_range(
806
+ int git_iterator_for_index(
395
807
  git_iterator **iter,
396
- git_repository *repo,
808
+ git_index *index,
809
+ git_iterator_flag_t flags,
397
810
  const char *start,
398
811
  const char *end)
399
812
  {
400
- int error;
401
813
  index_iterator *ii;
402
814
 
403
- ITERATOR_BASE_INIT(ii, index, INDEX);
815
+ ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
404
816
 
405
- if ((error = git_repository_index(&ii->index, repo)) < 0)
406
- git__free(ii);
407
- else {
408
- ii->base.ignore_case = ii->index->ignore_case;
409
- ii->current = start ? git_index__prefix_position(ii->index, start) : 0;
410
- *iter = (git_iterator *)ii;
817
+ if (index->ignore_case) {
818
+ ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
819
+ ii->base.prefixcomp = git__prefixcmp_icase;
411
820
  }
412
821
 
413
- return error;
822
+ ii->index = index;
823
+ GIT_REFCOUNT_INC(index);
824
+
825
+ git_buf_init(&ii->partial, 0);
826
+ ii->tree_entry.mode = GIT_FILEMODE_TREE;
827
+
828
+ index_iterator__reset((git_iterator *)ii, NULL, NULL);
829
+
830
+ *iter = (git_iterator *)ii;
831
+
832
+ return 0;
414
833
  }
415
834
 
416
835
 
836
+ #define WORKDIR_MAX_DEPTH 100
837
+
417
838
  typedef struct workdir_iterator_frame workdir_iterator_frame;
418
839
  struct workdir_iterator_frame {
419
840
  workdir_iterator_frame *next;
420
841
  git_vector entries;
421
- unsigned int index;
422
- char *start;
842
+ size_t index;
423
843
  };
424
844
 
425
845
  typedef struct {
426
846
  git_iterator base;
427
- git_repository *repo;
428
- size_t root_len;
847
+ git_iterator_callbacks cb;
429
848
  workdir_iterator_frame *stack;
430
849
  git_ignores ignores;
431
850
  git_index_entry entry;
432
851
  git_buf path;
852
+ size_t root_len;
433
853
  int is_ignored;
854
+ int depth;
434
855
  } workdir_iterator;
435
856
 
436
- static int git_path_with_stat_cmp_case(const void *a, const void *b)
437
- {
438
- const git_path_with_stat *path_with_stat_a = a;
439
- const git_path_with_stat *path_with_stat_b = b;
440
-
441
- return strcmp(path_with_stat_a->path, path_with_stat_b->path);
442
- }
443
-
444
- static int git_path_with_stat_cmp_icase(const void *a, const void *b)
857
+ GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
445
858
  {
446
- const git_path_with_stat *path_with_stat_a = a;
447
- const git_path_with_stat *path_with_stat_b = b;
448
-
449
- return strcasecmp(path_with_stat_a->path, path_with_stat_b->path);
859
+ if (!ps)
860
+ return false;
861
+ else {
862
+ const char *path = ps->path;
863
+ size_t len = ps->path_len;
864
+
865
+ if (len < 4)
866
+ return false;
867
+ if (path[len - 1] == '/')
868
+ len--;
869
+ if (tolower(path[len - 1]) != 't' ||
870
+ tolower(path[len - 2]) != 'i' ||
871
+ tolower(path[len - 3]) != 'g' ||
872
+ tolower(path[len - 4]) != '.')
873
+ return false;
874
+ return (len == 4 || path[len - 5] == '/');
875
+ }
450
876
  }
451
877
 
452
- static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi)
878
+ static workdir_iterator_frame *workdir_iterator__alloc_frame(
879
+ workdir_iterator *wi)
453
880
  {
454
881
  workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
455
- git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, git_path_with_stat_cmp_icase, git_path_with_stat_cmp_case);
882
+ git_vector_cmp entry_compare = CASESELECT(
883
+ iterator__ignore_case(wi),
884
+ git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
456
885
 
457
886
  if (wf == NULL)
458
887
  return NULL;
888
+
459
889
  if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
460
890
  git__free(wf);
461
891
  return NULL;
462
892
  }
893
+
463
894
  return wf;
464
895
  }
465
896
 
@@ -476,62 +907,73 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
476
907
 
477
908
  static int workdir_iterator__update_entry(workdir_iterator *wi);
478
909
 
479
- static int workdir_iterator__entry_cmp_case(const void *prefix, const void *item)
910
+ static int workdir_iterator__entry_cmp(const void *i, const void *item)
480
911
  {
912
+ const workdir_iterator *wi = (const workdir_iterator *)i;
481
913
  const git_path_with_stat *ps = item;
482
- return git__prefixcmp((const char *)prefix, ps->path);
914
+ return wi->base.prefixcomp(wi->base.start, ps->path);
483
915
  }
484
916
 
485
- static int workdir_iterator__entry_cmp_icase(const void *prefix, const void *item)
917
+ static void workdir_iterator__seek_frame_start(
918
+ workdir_iterator *wi, workdir_iterator_frame *wf)
486
919
  {
487
- const git_path_with_stat *ps = item;
488
- return git__prefixcmp_icase((const char *)prefix, ps->path);
920
+ if (!wf)
921
+ return;
922
+
923
+ if (wi->base.start)
924
+ git_vector_bsearch2(
925
+ &wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
926
+ else
927
+ wf->index = 0;
928
+
929
+ if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
930
+ wf->index++;
489
931
  }
490
932
 
491
933
  static int workdir_iterator__expand_dir(workdir_iterator *wi)
492
934
  {
493
935
  int error;
494
- workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi);
936
+ workdir_iterator_frame *wf;
937
+
938
+ wf = workdir_iterator__alloc_frame(wi);
495
939
  GITERR_CHECK_ALLOC(wf);
496
940
 
497
- error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
941
+ error = git_path_dirload_with_stat(
942
+ wi->path.ptr, wi->root_len, iterator__ignore_case(wi),
943
+ wi->base.start, wi->base.end, &wf->entries);
944
+
498
945
  if (error < 0 || wf->entries.length == 0) {
499
946
  workdir_iterator__free_frame(wf);
500
947
  return GIT_ENOTFOUND;
501
948
  }
502
949
 
503
- git_vector_sort(&wf->entries);
504
-
505
- if (!wi->stack)
506
- wf->start = wi->base.start;
507
- else if (wi->stack->start &&
508
- ITERATOR_PREFIXCMP(wi->base, wi->stack->start, wi->path.ptr + wi->root_len) == 0)
509
- wf->start = wi->stack->start;
510
-
511
- if (wf->start)
512
- git_vector_bsearch3(
513
- &wf->index,
514
- &wf->entries,
515
- CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case),
516
- wf->start);
950
+ if (++(wi->depth) > WORKDIR_MAX_DEPTH) {
951
+ giterr_set(GITERR_REPOSITORY,
952
+ "Working directory is too deep (%d)", wi->depth);
953
+ workdir_iterator__free_frame(wf);
954
+ return -1;
955
+ }
517
956
 
518
- wf->next = wi->stack;
519
- wi->stack = wf;
957
+ workdir_iterator__seek_frame_start(wi, wf);
520
958
 
521
959
  /* only push new ignores if this is not top level directory */
522
- if (wi->stack->next != NULL) {
960
+ if (wi->stack != NULL) {
523
961
  ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
524
962
  (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
525
963
  }
526
964
 
965
+ wf->next = wi->stack;
966
+ wi->stack = wf;
967
+
527
968
  return workdir_iterator__update_entry(wi);
528
969
  }
529
970
 
530
971
  static int workdir_iterator__current(
531
- git_iterator *self, const git_index_entry **entry)
972
+ const git_index_entry **entry, git_iterator *self)
532
973
  {
533
974
  workdir_iterator *wi = (workdir_iterator *)self;
534
- *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
975
+ if (entry)
976
+ *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
535
977
  return 0;
536
978
  }
537
979
 
@@ -540,46 +982,83 @@ static int workdir_iterator__at_end(git_iterator *self)
540
982
  return (((workdir_iterator *)self)->entry.path == NULL);
541
983
  }
542
984
 
985
+ static int workdir_iterator__advance_into(
986
+ const git_index_entry **entry, git_iterator *iter)
987
+ {
988
+ int error = 0;
989
+ workdir_iterator *wi = (workdir_iterator *)iter;
990
+
991
+ iterator__clear_entry(entry);
992
+
993
+ /* workdir iterator will allow you to explicitly advance into a
994
+ * commit/submodule (as well as a tree) to avoid some cases where an
995
+ * entry is mislabeled as a submodule in the working directory
996
+ */
997
+ if (wi->entry.path != NULL &&
998
+ (wi->entry.mode == GIT_FILEMODE_TREE ||
999
+ wi->entry.mode == GIT_FILEMODE_COMMIT))
1000
+ /* returns GIT_ENOTFOUND if the directory is empty */
1001
+ error = workdir_iterator__expand_dir(wi);
1002
+
1003
+ if (!error && entry)
1004
+ error = workdir_iterator__current(entry, iter);
1005
+
1006
+ return error;
1007
+ }
1008
+
543
1009
  static int workdir_iterator__advance(
544
- git_iterator *self, const git_index_entry **entry)
1010
+ const git_index_entry **entry, git_iterator *self)
545
1011
  {
546
- int error;
1012
+ int error = 0;
547
1013
  workdir_iterator *wi = (workdir_iterator *)self;
548
1014
  workdir_iterator_frame *wf;
549
1015
  git_path_with_stat *next;
550
1016
 
1017
+ /* given include_trees & autoexpand, we might have to go into a tree */
1018
+ if (iterator__do_autoexpand(wi) &&
1019
+ wi->entry.path != NULL &&
1020
+ wi->entry.mode == GIT_FILEMODE_TREE)
1021
+ {
1022
+ error = workdir_iterator__advance_into(entry, self);
1023
+
1024
+ /* continue silently past empty directories if autoexpanding */
1025
+ if (error != GIT_ENOTFOUND)
1026
+ return error;
1027
+ giterr_clear();
1028
+ error = 0;
1029
+ }
1030
+
551
1031
  if (entry != NULL)
552
1032
  *entry = NULL;
553
1033
 
554
- if (wi->entry.path == NULL)
555
- return 0;
556
-
557
- while ((wf = wi->stack) != NULL) {
1034
+ while (wi->entry.path != NULL) {
1035
+ wf = wi->stack;
558
1036
  next = git_vector_get(&wf->entries, ++wf->index);
1037
+
559
1038
  if (next != NULL) {
560
1039
  /* match git's behavior of ignoring anything named ".git" */
561
- if (STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT "/") == 0 ||
562
- STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT) == 0)
1040
+ if (path_is_dotgit(next))
563
1041
  continue;
564
1042
  /* else found a good entry */
565
1043
  break;
566
1044
  }
567
1045
 
568
- /* pop workdir directory stack */
569
- wi->stack = wf->next;
570
- workdir_iterator__free_frame(wf);
571
- git_ignore__pop_dir(&wi->ignores);
572
-
573
- if (wi->stack == NULL) {
1046
+ /* pop stack if anything is left to pop */
1047
+ if (!wf->next) {
574
1048
  memset(&wi->entry, 0, sizeof(wi->entry));
575
1049
  return 0;
576
1050
  }
1051
+
1052
+ wi->stack = wf->next;
1053
+ wi->depth--;
1054
+ workdir_iterator__free_frame(wf);
1055
+ git_ignore__pop_dir(&wi->ignores);
577
1056
  }
578
1057
 
579
1058
  error = workdir_iterator__update_entry(wi);
580
1059
 
581
1060
  if (!error && entry != NULL)
582
- error = workdir_iterator__current(self, entry);
1061
+ error = workdir_iterator__current(entry, self);
583
1062
 
584
1063
  return error;
585
1064
  }
@@ -594,18 +1073,25 @@ static int workdir_iterator__seek(git_iterator *self, const char *prefix)
594
1073
  return 0;
595
1074
  }
596
1075
 
597
- static int workdir_iterator__reset(git_iterator *self)
1076
+ static int workdir_iterator__reset(
1077
+ git_iterator *self, const char *start, const char *end)
598
1078
  {
599
1079
  workdir_iterator *wi = (workdir_iterator *)self;
1080
+
600
1081
  while (wi->stack != NULL && wi->stack->next != NULL) {
601
1082
  workdir_iterator_frame *wf = wi->stack;
602
1083
  wi->stack = wf->next;
603
1084
  workdir_iterator__free_frame(wf);
604
1085
  git_ignore__pop_dir(&wi->ignores);
605
1086
  }
606
- if (wi->stack)
607
- wi->stack->index = 0;
608
- return 0;
1087
+ wi->depth = 0;
1088
+
1089
+ if (iterator__reset_range(self, start, end) < 0)
1090
+ return -1;
1091
+
1092
+ workdir_iterator__seek_frame_start(wi, wi->stack);
1093
+
1094
+ return workdir_iterator__update_entry(wi);
609
1095
  }
610
1096
 
611
1097
  static void workdir_iterator__free(git_iterator *self)
@@ -624,7 +1110,9 @@ static void workdir_iterator__free(git_iterator *self)
624
1110
 
625
1111
  static int workdir_iterator__update_entry(workdir_iterator *wi)
626
1112
  {
627
- git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
1113
+ int error = 0;
1114
+ git_path_with_stat *ps =
1115
+ git_vector_get(&wi->stack->entries, wi->stack->index);
628
1116
 
629
1117
  git_buf_truncate(&wi->path, wi->root_len);
630
1118
  memset(&wi->entry, 0, sizeof(wi->entry));
@@ -632,84 +1120,78 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
632
1120
  if (!ps)
633
1121
  return 0;
634
1122
 
1123
+ /* skip over .git entries */
1124
+ if (path_is_dotgit(ps))
1125
+ return workdir_iterator__advance(NULL, (git_iterator *)wi);
1126
+
635
1127
  if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
636
1128
  return -1;
637
1129
 
638
- if (wi->base.end &&
639
- ITERATOR_PREFIXCMP(wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
1130
+ if (iterator__past_end(wi, wi->path.ptr + wi->root_len))
640
1131
  return 0;
641
1132
 
642
1133
  wi->entry.path = ps->path;
643
1134
 
644
- /* skip over .git entry */
645
- if (STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT "/") == 0 ||
646
- STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT) == 0)
647
- return workdir_iterator__advance((git_iterator *)wi, NULL);
648
-
649
- /* if there is an error processing the entry, treat as ignored */
650
- wi->is_ignored = 1;
1135
+ wi->is_ignored = -1;
651
1136
 
652
- git_index__init_entry_from_stat(&ps->st, &wi->entry);
1137
+ git_index_entry__init_from_stat(&wi->entry, &ps->st);
653
1138
 
654
1139
  /* need different mode here to keep directories during iteration */
655
1140
  wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
656
1141
 
657
1142
  /* if this is a file type we don't handle, treat as ignored */
658
- if (wi->entry.mode == 0)
1143
+ if (wi->entry.mode == 0) {
1144
+ wi->is_ignored = 1;
659
1145
  return 0;
1146
+ }
660
1147
 
661
- /* okay, we are far enough along to look up real ignore rule */
662
- if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
663
- return 0; /* if error, ignore it and ignore file */
1148
+ /* if this isn't a tree, then we're done */
1149
+ if (wi->entry.mode != GIT_FILEMODE_TREE)
1150
+ return 0;
664
1151
 
665
1152
  /* detect submodules */
666
- if (S_ISDIR(wi->entry.mode)) {
667
- int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
668
- bool is_submodule = (res == 0);
669
- if (res == GIT_ENOTFOUND)
670
- giterr_clear();
671
-
672
- /* if submodule, mark as GITLINK and remove trailing slash */
673
- if (is_submodule) {
674
- size_t len = strlen(wi->entry.path);
675
- assert(wi->entry.path[len - 1] == '/');
676
- wi->entry.path[len - 1] = '\0';
677
- wi->entry.mode = S_IFGITLINK;
678
- }
1153
+ error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path);
1154
+ if (error == GIT_ENOTFOUND)
1155
+ giterr_clear();
1156
+
1157
+ if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */
1158
+ error = 0;
1159
+
1160
+ /* if submodule, mark as GITLINK and remove trailing slash */
1161
+ if (!error) {
1162
+ size_t len = strlen(wi->entry.path);
1163
+ assert(wi->entry.path[len - 1] == '/');
1164
+ wi->entry.path[len - 1] = '\0';
1165
+ wi->entry.mode = S_IFGITLINK;
1166
+ return 0;
679
1167
  }
680
1168
 
681
- return 0;
1169
+ if (iterator__include_trees(wi))
1170
+ return 0;
1171
+
1172
+ return workdir_iterator__advance(NULL, (git_iterator *)wi);
682
1173
  }
683
1174
 
684
- int git_iterator_for_workdir_range(
1175
+ int git_iterator_for_workdir(
685
1176
  git_iterator **iter,
686
1177
  git_repository *repo,
1178
+ git_iterator_flag_t flags,
687
1179
  const char *start,
688
1180
  const char *end)
689
1181
  {
690
1182
  int error;
691
1183
  workdir_iterator *wi;
692
- git_index *index;
693
1184
 
694
1185
  assert(iter && repo);
695
1186
 
696
- if ((error = git_repository__ensure_not_bare(repo, "scan working directory")) < 0)
1187
+ if ((error = git_repository__ensure_not_bare(
1188
+ repo, "scan working directory")) < 0)
697
1189
  return error;
698
1190
 
699
- ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
1191
+ ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
700
1192
 
701
- wi->repo = repo;
702
-
703
- if ((error = git_repository_index(&index, repo)) < 0) {
704
- git__free(wi);
705
- return error;
706
- }
707
-
708
- /* Set the ignore_case flag for the workdir iterator to match
709
- * that of the index. */
710
- wi->base.ignore_case = index->ignore_case;
711
-
712
- git_index_free(index);
1193
+ if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
1194
+ goto fail;
713
1195
 
714
1196
  if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
715
1197
  git_path_to_dir(&wi->path) < 0 ||
@@ -718,244 +1200,150 @@ int git_iterator_for_workdir_range(
718
1200
  git__free(wi);
719
1201
  return -1;
720
1202
  }
721
-
722
1203
  wi->root_len = wi->path.size;
723
1204
 
724
1205
  if ((error = workdir_iterator__expand_dir(wi)) < 0) {
725
- if (error == GIT_ENOTFOUND)
726
- error = 0;
727
- else {
728
- git_iterator_free((git_iterator *)wi);
729
- wi = NULL;
730
- }
1206
+ if (error != GIT_ENOTFOUND)
1207
+ goto fail;
1208
+ giterr_clear();
731
1209
  }
732
1210
 
733
1211
  *iter = (git_iterator *)wi;
1212
+ return 0;
734
1213
 
1214
+ fail:
1215
+ git_iterator_free((git_iterator *)wi);
735
1216
  return error;
736
1217
  }
737
1218
 
738
- typedef struct {
739
- git_iterator base;
740
- git_iterator *wrapped;
741
- git_vector entries;
742
- git_vector_cmp comparer;
743
- git_pool entry_pool;
744
- git_pool string_pool;
745
- unsigned int position;
746
- } spoolandsort_iterator;
747
-
748
- static int spoolandsort_iterator__current(
749
- git_iterator *self, const git_index_entry **entry)
750
- {
751
- spoolandsort_iterator *si = (spoolandsort_iterator *)self;
752
-
753
- if (si->position < si->entries.length)
754
- *entry = (const git_index_entry *)git_vector_get_const(&si->entries, si->position);
755
- else
756
- *entry = NULL;
757
-
758
- return 0;
759
- }
760
1219
 
761
- static int spoolandsort_iterator__at_end(git_iterator *self)
1220
+ void git_iterator_free(git_iterator *iter)
762
1221
  {
763
- spoolandsort_iterator *si = (spoolandsort_iterator *)self;
1222
+ if (iter == NULL)
1223
+ return;
764
1224
 
765
- return 0 == si->entries.length || si->entries.length - 1 <= si->position;
766
- }
1225
+ iter->cb->free(iter);
767
1226
 
768
- static int spoolandsort_iterator__advance(
769
- git_iterator *self, const git_index_entry **entry)
770
- {
771
- spoolandsort_iterator *si = (spoolandsort_iterator *)self;
1227
+ git__free(iter->start);
1228
+ git__free(iter->end);
772
1229
 
773
- if (si->position < si->entries.length)
774
- *entry = (const git_index_entry *)git_vector_get_const(&si->entries, ++si->position);
775
- else
776
- *entry = NULL;
1230
+ memset(iter, 0, sizeof(*iter));
777
1231
 
778
- return 0;
1232
+ git__free(iter);
779
1233
  }
780
1234
 
781
- static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix)
1235
+ int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
782
1236
  {
783
- GIT_UNUSED(self);
784
- GIT_UNUSED(prefix);
785
-
786
- return -1;
787
- }
1237
+ bool desire_ignore_case = (ignore_case != 0);
788
1238
 
789
- static int spoolandsort_iterator__reset(git_iterator *self)
790
- {
791
- spoolandsort_iterator *si = (spoolandsort_iterator *)self;
1239
+ if (iterator__ignore_case(iter) == desire_ignore_case)
1240
+ return 0;
792
1241
 
793
- si->position = 0;
1242
+ if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1243
+ if (desire_ignore_case)
1244
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1245
+ else
1246
+ iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1247
+ } else {
1248
+ giterr_set(GITERR_INVALID,
1249
+ "Cannot currently set ignore case on non-empty iterators");
1250
+ return -1;
1251
+ }
794
1252
 
795
1253
  return 0;
796
1254
  }
797
1255
 
798
- static void spoolandsort_iterator__free(git_iterator *self)
1256
+ git_index *git_iterator_get_index(git_iterator *iter)
799
1257
  {
800
- spoolandsort_iterator *si = (spoolandsort_iterator *)self;
801
-
802
- git_pool_clear(&si->string_pool);
803
- git_pool_clear(&si->entry_pool);
804
- git_vector_free(&si->entries);
805
- git_iterator_free(si->wrapped);
1258
+ if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1259
+ return ((index_iterator *)iter)->index;
1260
+ return NULL;
806
1261
  }
807
1262
 
808
- int git_iterator_spoolandsort_range(
809
- git_iterator **iter,
810
- git_iterator *towrap,
811
- git_vector_cmp comparer,
812
- bool ignore_case,
813
- const char *start,
814
- const char *end)
1263
+ int git_iterator_current_tree_entry(
1264
+ const git_tree_entry **tree_entry, git_iterator *iter)
815
1265
  {
816
- spoolandsort_iterator *si;
817
- const git_index_entry *item;
818
-
819
- assert(iter && towrap && comparer);
820
-
821
- ITERATOR_BASE_INIT(si, spoolandsort, SPOOLANDSORT);
822
- si->base.ignore_case = ignore_case;
823
- si->wrapped = towrap;
824
- si->comparer = comparer;
825
- si->position = 0;
826
-
827
- if (git_vector_init(&si->entries, 16, si->comparer) < 0 ||
828
- git_iterator_current(towrap, &item) < 0 ||
829
- git_pool_init(&si->entry_pool, sizeof(git_index_entry), 0) ||
830
- git_pool_init(&si->string_pool, 1, 0))
831
- {
832
- git__free(si);
833
- return -1;
834
- }
835
-
836
- while (item)
837
- {
838
- git_index_entry *clone = git_pool_malloc(&si->entry_pool, 1);
839
- memcpy(clone, item, sizeof(git_index_entry));
840
-
841
- if (item->path)
842
- {
843
- clone->path = git_pool_strdup(&si->string_pool, item->path);
844
- }
845
-
846
- git_vector_insert(&si->entries, clone);
847
-
848
- if (git_iterator_advance(towrap, &item) < 0)
849
- {
850
- git__free(si);
851
- return -1;
852
- }
1266
+ if (iter->type != GIT_ITERATOR_TYPE_TREE)
1267
+ *tree_entry = NULL;
1268
+ else {
1269
+ tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1270
+ *tree_entry = (tf->current < tf->n_entries) ?
1271
+ tf->entries[tf->current]->te : NULL;
853
1272
  }
854
1273
 
855
- git_vector_sort(&si->entries);
856
-
857
- *iter = (git_iterator *)si;
858
-
859
- return 0;
860
- }
861
-
862
- int git_iterator_current_tree_entry(
863
- git_iterator *iter, const git_tree_entry **tree_entry)
864
- {
865
- *tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :
866
- tree_iterator__tree_entry((tree_iterator *)iter);
867
1274
  return 0;
868
1275
  }
869
1276
 
870
1277
  int git_iterator_current_parent_tree(
1278
+ const git_tree **tree_ptr,
871
1279
  git_iterator *iter,
872
- const char *parent_path,
873
- const git_tree **tree_ptr)
1280
+ const char *parent_path)
874
1281
  {
875
1282
  tree_iterator *ti = (tree_iterator *)iter;
876
1283
  tree_iterator_frame *tf;
877
1284
  const char *scan = parent_path;
1285
+ const git_tree_entry *te;
878
1286
 
879
- if (iter->type != GIT_ITERATOR_TREE || ti->stack == NULL)
880
- goto notfound;
1287
+ *tree_ptr = NULL;
881
1288
 
882
- for (tf = ti->tail; tf != NULL; tf = tf->prev) {
883
- const git_tree_entry *te;
1289
+ if (iter->type != GIT_ITERATOR_TYPE_TREE)
1290
+ return 0;
884
1291
 
885
- if (!*scan) {
886
- *tree_ptr = tf->tree;
1292
+ for (tf = ti->root; *scan; ) {
1293
+ if (!(tf = tf->down) ||
1294
+ tf->current >= tf->n_entries ||
1295
+ !(te = tf->entries[tf->current]->te) ||
1296
+ ti->strncomp(scan, te->filename, te->filename_len) != 0)
887
1297
  return 0;
888
- }
889
-
890
- te = git_tree_entry_byindex(tf->tree, tf->index);
891
-
892
- if (strncmp(scan, te->filename, te->filename_len) != 0)
893
- goto notfound;
894
1298
 
895
1299
  scan += te->filename_len;
896
-
897
- if (*scan) {
898
- if (*scan != '/')
899
- goto notfound;
1300
+ if (*scan == '/')
900
1301
  scan++;
901
- }
902
1302
  }
903
1303
 
904
- notfound:
905
- *tree_ptr = NULL;
1304
+ *tree_ptr = tf->entries[tf->current]->tree;
906
1305
  return 0;
907
1306
  }
908
1307
 
909
- int git_iterator_current_is_ignored(git_iterator *iter)
910
- {
911
- return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :
912
- ((workdir_iterator *)iter)->is_ignored;
913
- }
914
-
915
- int git_iterator_advance_into_directory(
916
- git_iterator *iter, const git_index_entry **entry)
1308
+ bool git_iterator_current_is_ignored(git_iterator *iter)
917
1309
  {
918
1310
  workdir_iterator *wi = (workdir_iterator *)iter;
919
1311
 
920
- if (iter->type == GIT_ITERATOR_WORKDIR &&
921
- wi->entry.path &&
922
- S_ISDIR(wi->entry.mode) &&
923
- !S_ISGITLINK(wi->entry.mode))
924
- {
925
- if (workdir_iterator__expand_dir(wi) < 0)
926
- /* if error loading or if empty, skip the directory. */
927
- return workdir_iterator__advance(iter, entry);
928
- }
1312
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1313
+ return false;
1314
+
1315
+ if (wi->is_ignored != -1)
1316
+ return (bool)(wi->is_ignored != 0);
929
1317
 
930
- return entry ? git_iterator_current(iter, entry) : 0;
1318
+ if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
1319
+ wi->is_ignored = true;
1320
+
1321
+ return (bool)wi->is_ignored;
931
1322
  }
932
1323
 
933
- int git_iterator_cmp(
934
- git_iterator *iter, const char *path_prefix)
1324
+ int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
935
1325
  {
936
1326
  const git_index_entry *entry;
937
1327
 
938
1328
  /* a "done" iterator is after every prefix */
939
- if (git_iterator_current(iter, &entry) < 0 ||
940
- entry == NULL)
1329
+ if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
941
1330
  return 1;
942
1331
 
943
1332
  /* a NULL prefix is after any valid iterator */
944
1333
  if (!path_prefix)
945
1334
  return -1;
946
1335
 
947
- return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix);
1336
+ return iter->prefixcomp(entry->path, path_prefix);
948
1337
  }
949
1338
 
950
- int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path)
1339
+ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
951
1340
  {
952
1341
  workdir_iterator *wi = (workdir_iterator *)iter;
953
1342
 
954
- if (iter->type != GIT_ITERATOR_WORKDIR || !wi->entry.path)
1343
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path)
955
1344
  *path = NULL;
956
1345
  else
957
1346
  *path = &wi->path;
958
1347
 
959
1348
  return 0;
960
1349
  }
961
-