rugged 0.17.0.b7 → 0.18.0.b1

Sign up to get free protection for your applications and to get access to all the features.
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
-