rugged 0.18.0.gh.de28323 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. data/README.md +9 -4
  2. data/Rakefile +1 -1
  3. data/ext/rugged/extconf.rb +10 -0
  4. data/ext/rugged/rugged.c +153 -86
  5. data/ext/rugged/rugged.h +44 -33
  6. data/ext/rugged/rugged_blob.c +288 -60
  7. data/ext/rugged/rugged_branch.c +82 -57
  8. data/ext/rugged/rugged_commit.c +83 -86
  9. data/ext/rugged/rugged_config.c +68 -68
  10. data/ext/rugged/rugged_diff.c +509 -0
  11. data/ext/rugged/rugged_diff_delta.c +94 -0
  12. data/ext/rugged/rugged_diff_hunk.c +100 -0
  13. data/ext/rugged/rugged_diff_line.c +79 -0
  14. data/ext/rugged/rugged_diff_patch.c +169 -0
  15. data/ext/rugged/rugged_index.c +539 -8
  16. data/ext/rugged/rugged_note.c +74 -80
  17. data/ext/rugged/rugged_object.c +63 -8
  18. data/ext/rugged/rugged_reference.c +231 -145
  19. data/ext/rugged/rugged_remote.c +509 -53
  20. data/ext/rugged/rugged_repo.c +572 -236
  21. data/ext/rugged/rugged_revwalk.c +59 -36
  22. data/ext/rugged/rugged_settings.c +7 -9
  23. data/ext/rugged/rugged_signature.c +7 -11
  24. data/ext/rugged/rugged_tag.c +93 -39
  25. data/ext/rugged/rugged_tree.c +321 -58
  26. data/lib/rugged.rb +1 -0
  27. data/lib/rugged/commit.rb +16 -1
  28. data/lib/rugged/console.rb +9 -0
  29. data/lib/rugged/diff.rb +19 -0
  30. data/lib/rugged/diff/delta.rb +54 -0
  31. data/lib/rugged/diff/hunk.rb +23 -0
  32. data/lib/rugged/diff/line.rb +29 -0
  33. data/lib/rugged/diff/patch.rb +28 -0
  34. data/lib/rugged/repository.rb +36 -39
  35. data/lib/rugged/version.rb +1 -1
  36. data/test/blob_test.rb +308 -1
  37. data/test/branch_test.rb +7 -0
  38. data/test/commit_test.rb +7 -10
  39. data/test/coverage/cover.rb +9 -1
  40. data/test/diff_test.rb +777 -0
  41. data/test/fixtures/archive.tar.gz +0 -0
  42. data/test/fixtures/attr/attr0 +1 -0
  43. data/test/fixtures/attr/attr1 +29 -0
  44. data/test/fixtures/attr/attr2 +21 -0
  45. data/test/fixtures/attr/attr3 +4 -0
  46. data/test/fixtures/attr/binfile +1 -0
  47. data/test/fixtures/attr/dir/file +0 -0
  48. data/test/fixtures/attr/file +1 -0
  49. data/test/fixtures/attr/gitattributes +29 -0
  50. data/test/fixtures/attr/gitignore +2 -0
  51. data/test/fixtures/attr/ign +1 -0
  52. data/test/fixtures/attr/macro_bad +1 -0
  53. data/test/fixtures/attr/macro_test +1 -0
  54. data/test/fixtures/attr/root_test1 +1 -0
  55. data/test/fixtures/attr/root_test2 +6 -0
  56. data/test/fixtures/attr/root_test3 +19 -0
  57. data/test/fixtures/attr/root_test4.txt +14 -0
  58. data/test/fixtures/attr/sub/abc +37 -0
  59. data/test/fixtures/attr/sub/dir/file +0 -0
  60. data/test/fixtures/attr/sub/file +1 -0
  61. data/test/fixtures/attr/sub/ign/file +1 -0
  62. data/test/fixtures/attr/sub/ign/sub/file +1 -0
  63. data/test/fixtures/attr/sub/sub/dir +0 -0
  64. data/test/fixtures/attr/sub/sub/file +1 -0
  65. data/test/fixtures/attr/sub/sub/subsub.txt +1 -0
  66. data/test/fixtures/attr/sub/subdir_test1 +2 -0
  67. data/test/fixtures/attr/sub/subdir_test2.txt +1 -0
  68. data/test/fixtures/diff/another.txt +38 -0
  69. data/test/fixtures/diff/readme.txt +36 -0
  70. data/test/fixtures/mergedrepo/conflicts-one.txt +5 -0
  71. data/test/fixtures/mergedrepo/conflicts-two.txt +5 -0
  72. data/test/fixtures/mergedrepo/one.txt +10 -0
  73. data/test/fixtures/mergedrepo/two.txt +12 -0
  74. data/test/fixtures/status/current_file +1 -0
  75. data/test/fixtures/status/ignored_file +1 -0
  76. data/test/fixtures/status/modified_file +2 -0
  77. data/test/fixtures/status/new_file +1 -0
  78. data/test/fixtures/status/staged_changes +2 -0
  79. data/test/fixtures/status/staged_changes_modified_file +3 -0
  80. data/test/fixtures/status/staged_delete_modified_file +1 -0
  81. data/test/fixtures/status/staged_new_file +1 -0
  82. data/test/fixtures/status/staged_new_file_modified_file +2 -0
  83. data/test/fixtures/status/subdir.txt +2 -0
  84. data/test/fixtures/status/subdir/current_file +1 -0
  85. data/test/fixtures/status/subdir/modified_file +2 -0
  86. data/test/fixtures/status/subdir/new_file +1 -0
  87. data/test/fixtures/status//350/277/231 +1 -0
  88. data/test/fixtures/testrepo.git/config +5 -0
  89. data/test/fixtures/testrepo.git/objects/77/71329dfa3002caf8c61a0ceb62a31d09023f37 +0 -0
  90. data/test/fixtures/text_file.md +464 -0
  91. data/test/fixtures/unsymlinked.git/HEAD +1 -0
  92. data/test/fixtures/unsymlinked.git/config +6 -0
  93. data/test/fixtures/unsymlinked.git/description +1 -0
  94. data/test/fixtures/unsymlinked.git/info/exclude +2 -0
  95. data/test/fixtures/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf +0 -0
  96. data/test/fixtures/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b +0 -0
  97. data/test/fixtures/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c +0 -0
  98. data/test/fixtures/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 +0 -0
  99. data/test/fixtures/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c +0 -0
  100. data/test/fixtures/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 +0 -0
  101. data/test/fixtures/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d +0 -0
  102. data/test/fixtures/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 +0 -0
  103. data/test/fixtures/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 +0 -0
  104. data/test/fixtures/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 +2 -0
  105. data/test/fixtures/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 +0 -0
  106. data/test/fixtures/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a +0 -0
  107. data/test/fixtures/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 +0 -0
  108. data/test/fixtures/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 +0 -0
  109. data/test/fixtures/unsymlinked.git/refs/heads/exe-file +1 -0
  110. data/test/fixtures/unsymlinked.git/refs/heads/master +1 -0
  111. data/test/fixtures/unsymlinked.git/refs/heads/reg-file +1 -0
  112. data/test/index_test.rb +120 -0
  113. data/test/reference_test.rb +38 -3
  114. data/test/remote_test.rb +224 -3
  115. data/test/repo_reset_test.rb +2 -0
  116. data/test/repo_test.rb +147 -10
  117. data/test/test_helper.rb +5 -2
  118. data/vendor/libgit2/include/git2/attr.h +3 -3
  119. data/vendor/libgit2/include/git2/blob.h +11 -17
  120. data/vendor/libgit2/include/git2/branch.h +3 -2
  121. data/vendor/libgit2/include/git2/checkout.h +7 -0
  122. data/vendor/libgit2/include/git2/clone.h +3 -0
  123. data/vendor/libgit2/include/git2/commit.h +61 -66
  124. data/vendor/libgit2/include/git2/common.h +73 -42
  125. data/vendor/libgit2/include/git2/config.h +57 -71
  126. data/vendor/libgit2/include/git2/cred_helpers.h +2 -2
  127. data/vendor/libgit2/include/git2/diff.h +179 -30
  128. data/vendor/libgit2/include/git2/errors.h +3 -3
  129. data/vendor/libgit2/include/git2/index.h +225 -146
  130. data/vendor/libgit2/include/git2/indexer.h +2 -22
  131. data/vendor/libgit2/include/git2/inttypes.h +9 -9
  132. data/vendor/libgit2/include/git2/merge.h +123 -5
  133. data/vendor/libgit2/include/git2/odb.h +59 -38
  134. data/vendor/libgit2/include/git2/odb_backend.h +45 -104
  135. data/vendor/libgit2/include/git2/oid.h +30 -19
  136. data/vendor/libgit2/include/git2/pack.h +21 -3
  137. data/vendor/libgit2/include/git2/refdb.h +0 -35
  138. data/vendor/libgit2/include/git2/refs.h +93 -31
  139. data/vendor/libgit2/include/git2/refspec.h +17 -0
  140. data/vendor/libgit2/include/git2/remote.h +60 -20
  141. data/vendor/libgit2/include/git2/repository.h +48 -70
  142. data/vendor/libgit2/include/git2/reset.h +3 -3
  143. data/vendor/libgit2/include/git2/revparse.h +22 -0
  144. data/vendor/libgit2/include/git2/stash.h +1 -1
  145. data/vendor/libgit2/include/git2/status.h +131 -56
  146. data/vendor/libgit2/include/git2/strarray.h +2 -2
  147. data/vendor/libgit2/include/git2/submodule.h +16 -16
  148. data/vendor/libgit2/include/git2/sys/commit.h +46 -0
  149. data/vendor/libgit2/include/git2/sys/config.h +71 -0
  150. data/vendor/libgit2/include/git2/sys/index.h +179 -0
  151. data/vendor/libgit2/include/git2/sys/odb_backend.h +86 -0
  152. data/vendor/libgit2/include/git2/sys/refdb_backend.h +158 -0
  153. data/vendor/libgit2/include/git2/sys/refs.h +38 -0
  154. data/vendor/libgit2/include/git2/sys/repository.h +106 -0
  155. data/vendor/libgit2/include/git2/tag.h +44 -18
  156. data/vendor/libgit2/include/git2/trace.h +1 -2
  157. data/vendor/libgit2/include/git2/transport.h +74 -0
  158. data/vendor/libgit2/include/git2/tree.h +12 -22
  159. data/vendor/libgit2/include/git2/types.h +33 -0
  160. data/vendor/libgit2/include/git2/version.h +2 -2
  161. data/vendor/libgit2/src/array.h +66 -0
  162. data/vendor/libgit2/src/attr.c +26 -13
  163. data/vendor/libgit2/src/attr_file.c +3 -2
  164. data/vendor/libgit2/src/attr_file.h +3 -3
  165. data/vendor/libgit2/src/attrcache.h +4 -4
  166. data/vendor/libgit2/src/blob.c +13 -9
  167. data/vendor/libgit2/src/blob.h +2 -2
  168. data/vendor/libgit2/src/branch.c +67 -49
  169. data/vendor/libgit2/src/cache.c +224 -54
  170. data/vendor/libgit2/src/cache.h +33 -20
  171. data/vendor/libgit2/src/checkout.c +145 -85
  172. data/vendor/libgit2/src/clone.c +62 -50
  173. data/vendor/libgit2/src/commit.c +74 -40
  174. data/vendor/libgit2/src/commit.h +2 -3
  175. data/vendor/libgit2/src/commit_list.c +14 -8
  176. data/vendor/libgit2/src/config.c +119 -36
  177. data/vendor/libgit2/src/config.h +3 -0
  178. data/vendor/libgit2/src/config_cache.c +24 -7
  179. data/vendor/libgit2/src/config_file.c +9 -6
  180. data/vendor/libgit2/src/crlf.c +4 -2
  181. data/vendor/libgit2/src/date.c +3 -3
  182. data/vendor/libgit2/src/delta.c +1 -1
  183. data/vendor/libgit2/src/diff.c +681 -303
  184. data/vendor/libgit2/src/diff.h +34 -2
  185. data/vendor/libgit2/src/diff_driver.c +405 -0
  186. data/vendor/libgit2/src/diff_driver.h +49 -0
  187. data/vendor/libgit2/src/diff_file.c +447 -0
  188. data/vendor/libgit2/src/diff_file.h +58 -0
  189. data/vendor/libgit2/src/diff_patch.c +995 -0
  190. data/vendor/libgit2/src/diff_patch.h +46 -0
  191. data/vendor/libgit2/src/diff_print.c +430 -0
  192. data/vendor/libgit2/src/diff_tform.c +464 -203
  193. data/vendor/libgit2/src/diff_xdiff.c +166 -0
  194. data/vendor/libgit2/src/diff_xdiff.h +28 -0
  195. data/vendor/libgit2/src/fetch.c +11 -4
  196. data/vendor/libgit2/src/fileops.c +85 -61
  197. data/vendor/libgit2/src/fileops.h +4 -0
  198. data/vendor/libgit2/src/global.c +10 -2
  199. data/vendor/libgit2/src/global.h +0 -8
  200. data/vendor/libgit2/src/hash/hash_generic.h +3 -3
  201. data/vendor/libgit2/src/hash/hash_win32.h +4 -4
  202. data/vendor/libgit2/src/hashsig.c +0 -1
  203. data/vendor/libgit2/src/ignore.c +68 -28
  204. data/vendor/libgit2/src/ignore.h +10 -1
  205. data/vendor/libgit2/src/index.c +666 -84
  206. data/vendor/libgit2/src/index.h +6 -0
  207. data/vendor/libgit2/src/indexer.c +10 -28
  208. data/vendor/libgit2/src/iterator.c +427 -283
  209. data/vendor/libgit2/src/iterator.h +58 -4
  210. data/vendor/libgit2/src/merge.c +1892 -32
  211. data/vendor/libgit2/src/merge.h +132 -5
  212. data/vendor/libgit2/src/merge_file.c +174 -0
  213. data/vendor/libgit2/src/merge_file.h +71 -0
  214. data/vendor/libgit2/src/mwindow.c +1 -1
  215. data/vendor/libgit2/src/notes.c +45 -48
  216. data/vendor/libgit2/src/object.c +89 -127
  217. data/vendor/libgit2/src/object.h +0 -1
  218. data/vendor/libgit2/src/object_api.c +129 -0
  219. data/vendor/libgit2/src/odb.c +156 -59
  220. data/vendor/libgit2/src/odb.h +5 -2
  221. data/vendor/libgit2/src/odb_loose.c +31 -17
  222. data/vendor/libgit2/src/odb_pack.c +39 -43
  223. data/vendor/libgit2/src/oid.c +62 -27
  224. data/vendor/libgit2/src/oid.h +33 -0
  225. data/vendor/libgit2/src/oidmap.h +4 -6
  226. data/vendor/libgit2/src/pack-objects.c +54 -22
  227. data/vendor/libgit2/src/pack.c +98 -56
  228. data/vendor/libgit2/src/pack.h +3 -1
  229. data/vendor/libgit2/src/pathspec.c +26 -1
  230. data/vendor/libgit2/src/pathspec.h +14 -0
  231. data/vendor/libgit2/src/pool.c +5 -0
  232. data/vendor/libgit2/src/posix.c +2 -2
  233. data/vendor/libgit2/src/posix.h +3 -0
  234. data/vendor/libgit2/src/push.c +13 -10
  235. data/vendor/libgit2/src/refdb.c +82 -62
  236. data/vendor/libgit2/src/refdb.h +16 -16
  237. data/vendor/libgit2/src/refdb_fs.c +386 -133
  238. data/vendor/libgit2/src/reflog.c +3 -1
  239. data/vendor/libgit2/src/refs.c +247 -221
  240. data/vendor/libgit2/src/refs.h +2 -1
  241. data/vendor/libgit2/src/refspec.c +18 -1
  242. data/vendor/libgit2/src/refspec.h +3 -1
  243. data/vendor/libgit2/src/remote.c +434 -253
  244. data/vendor/libgit2/src/remote.h +5 -3
  245. data/vendor/libgit2/src/repository.c +197 -111
  246. data/vendor/libgit2/src/repository.h +26 -5
  247. data/vendor/libgit2/src/reset.c +1 -1
  248. data/vendor/libgit2/src/revparse.c +84 -79
  249. data/vendor/libgit2/src/revwalk.c +1 -1
  250. data/vendor/libgit2/src/signature.c +22 -10
  251. data/vendor/libgit2/src/stash.c +5 -2
  252. data/vendor/libgit2/src/status.c +311 -107
  253. data/vendor/libgit2/src/status.h +23 -0
  254. data/vendor/libgit2/src/submodule.c +21 -13
  255. data/vendor/libgit2/src/tag.c +42 -31
  256. data/vendor/libgit2/src/tag.h +2 -3
  257. data/vendor/libgit2/src/thread-utils.h +105 -3
  258. data/vendor/libgit2/src/trace.c +1 -2
  259. data/vendor/libgit2/src/trace.h +3 -3
  260. data/vendor/libgit2/src/transport.c +18 -6
  261. data/vendor/libgit2/src/transports/cred.c +103 -1
  262. data/vendor/libgit2/src/transports/local.c +19 -9
  263. data/vendor/libgit2/src/transports/smart_protocol.c +32 -12
  264. data/vendor/libgit2/src/transports/ssh.c +519 -0
  265. data/vendor/libgit2/src/transports/winhttp.c +3 -1
  266. data/vendor/libgit2/src/tree.c +26 -28
  267. data/vendor/libgit2/src/tree.h +3 -3
  268. data/vendor/libgit2/src/unix/posix.h +2 -0
  269. data/vendor/libgit2/src/util.c +43 -6
  270. data/vendor/libgit2/src/util.h +40 -12
  271. data/vendor/libgit2/src/vector.c +3 -5
  272. data/vendor/libgit2/src/vector.h +9 -0
  273. data/vendor/libgit2/src/win32/dir.c +1 -1
  274. data/vendor/libgit2/src/win32/error.c +2 -0
  275. data/vendor/libgit2/src/win32/findfile.c +3 -6
  276. data/vendor/libgit2/src/win32/posix_w32.c +85 -59
  277. data/vendor/libgit2/src/win32/pthread.c +16 -8
  278. data/vendor/libgit2/src/win32/pthread.h +7 -4
  279. metadata +407 -306
  280. data/test/coverage/HEAD.json +0 -1
  281. data/vendor/libgit2/include/git2/refdb_backend.h +0 -109
  282. data/vendor/libgit2/src/diff_output.c +0 -1819
  283. data/vendor/libgit2/src/diff_output.h +0 -93
@@ -33,6 +33,7 @@ struct git_index {
33
33
 
34
34
  git_tree_cache *tree;
35
35
 
36
+ git_vector names;
36
37
  git_vector reuc;
37
38
 
38
39
  git_vector_cmp entries_cmp_path;
@@ -41,6 +42,11 @@ struct git_index {
41
42
  git_vector_cmp reuc_search;
42
43
  };
43
44
 
45
+ struct git_index_conflict_iterator {
46
+ git_index *index;
47
+ size_t cur;
48
+ };
49
+
44
50
  extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
45
51
 
46
52
  extern size_t git_index__prefix_position(git_index *index, const char *path);
@@ -9,7 +9,6 @@
9
9
 
10
10
  #include "git2/indexer.h"
11
11
  #include "git2/object.h"
12
- #include "git2/oid.h"
13
12
 
14
13
  #include "common.h"
15
14
  #include "pack.h"
@@ -17,6 +16,7 @@
17
16
  #include "posix.h"
18
17
  #include "pack.h"
19
18
  #include "filebuf.h"
19
+ #include "oid.h"
20
20
  #include "oidmap.h"
21
21
 
22
22
  #define UINT31_MAX (0x7FFFFFFF)
@@ -60,36 +60,19 @@ const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx)
60
60
 
61
61
  static int open_pack(struct git_pack_file **out, const char *filename)
62
62
  {
63
- size_t namelen;
64
63
  struct git_pack_file *pack;
65
- struct stat st;
66
- int fd;
67
64
 
68
- namelen = strlen(filename);
69
- pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);
70
- GITERR_CHECK_ALLOC(pack);
71
-
72
- memcpy(pack->pack_name, filename, namelen + 1);
73
-
74
- if (p_stat(filename, &st) < 0) {
75
- giterr_set(GITERR_OS, "Failed to stat packfile.");
76
- goto cleanup;
77
- }
65
+ if (git_packfile_alloc(&pack, filename) < 0)
66
+ return -1;
78
67
 
79
- if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
68
+ if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
80
69
  giterr_set(GITERR_OS, "Failed to open packfile.");
81
- goto cleanup;
70
+ git_packfile_free(pack);
71
+ return -1;
82
72
  }
83
73
 
84
- pack->mwf.fd = fd;
85
- pack->mwf.size = (git_off_t)st.st_size;
86
-
87
74
  *out = pack;
88
75
  return 0;
89
-
90
- cleanup:
91
- git__free(pack);
92
- return -1;
93
76
  }
94
77
 
95
78
  static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
@@ -120,7 +103,7 @@ static int objects_cmp(const void *a, const void *b)
120
103
  const struct entry *entrya = a;
121
104
  const struct entry *entryb = b;
122
105
 
123
- return git_oid_cmp(&entrya->oid, &entryb->oid);
106
+ return git_oid__cmp(&entrya->oid, &entryb->oid);
124
107
  }
125
108
 
126
109
  int git_indexer_stream_new(
@@ -276,7 +259,7 @@ static int store_object(git_indexer_stream *idx)
276
259
  entry = git__calloc(1, sizeof(*entry));
277
260
  GITERR_CHECK_ALLOC(entry);
278
261
 
279
- pentry = git__malloc(sizeof(struct git_pack_entry));
262
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
280
263
  GITERR_CHECK_ALLOC(pentry);
281
264
 
282
265
  git_hash_final(&oid, ctx);
@@ -345,7 +328,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
345
328
  return -1;
346
329
  }
347
330
 
348
- pentry = git__malloc(sizeof(struct git_pack_entry));
331
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
349
332
  GITERR_CHECK_ALLOC(pentry);
350
333
 
351
334
  git_oid_cpy(&pentry->sha1, &oid);
@@ -391,7 +374,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
391
374
  {
392
375
  int error = -1;
393
376
  struct git_pack_header hdr;
394
- size_t processed;
377
+ size_t processed;
395
378
  git_mwindow_file *mwf = &idx->pack->mwf;
396
379
 
397
380
  assert(idx && data && stats);
@@ -404,7 +387,6 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
404
387
  /* Make sure we set the new size of the pack */
405
388
  if (idx->opened_pack) {
406
389
  idx->pack->mwf.size += size;
407
- //printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);
408
390
  } else {
409
391
  if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)
410
392
  return -1;
@@ -7,6 +7,7 @@
7
7
 
8
8
  #include "iterator.h"
9
9
  #include "tree.h"
10
+ #include "index.h"
10
11
  #include "ignore.h"
11
12
  #include "buffer.h"
12
13
  #include "git2/submodule.h"
@@ -26,8 +27,6 @@
26
27
  (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
27
28
 
28
29
  #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
29
- (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
30
- GITERR_CHECK_ALLOC(P); \
31
30
  (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
32
31
  (P)->base.cb = &(P)->cb; \
33
32
  ITERATOR_SET_CB(P,NAME_LC); \
@@ -48,6 +47,9 @@
48
47
  #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
49
48
  #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
50
49
 
50
+ #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
51
+ #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
52
+
51
53
  #define iterator__end(I) ((git_iterator *)(I))->end
52
54
  #define iterator__past_end(I,PATH) \
53
55
  (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
@@ -70,6 +72,8 @@ static int iterator__reset_range(
70
72
  GITERR_CHECK_ALLOC(iter->end);
71
73
  }
72
74
 
75
+ iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
76
+
73
77
  return 0;
74
78
  }
75
79
 
@@ -111,7 +115,7 @@ static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
111
115
  {
112
116
  GIT_UNUSED(i);
113
117
  iterator__clear_entry(e);
114
- return 0;
118
+ return GIT_ITEROVER;
115
119
  }
116
120
 
117
121
  static int empty_iterator__seek(git_iterator *i, const char *p)
@@ -148,7 +152,8 @@ int git_iterator_for_nothing(
148
152
  const char *start,
149
153
  const char *end)
150
154
  {
151
- empty_iterator *i;
155
+ empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
156
+ GITERR_CHECK_ALLOC(i);
152
157
 
153
158
  #define empty_iterator__current empty_iterator__noop
154
159
  #define empty_iterator__advance empty_iterator__noop
@@ -194,6 +199,7 @@ typedef struct {
194
199
  git_buf path;
195
200
  int path_ambiguities;
196
201
  bool path_has_filename;
202
+ bool entry_is_current;
197
203
  int (*strncomp)(const char *a, const char *b, size_t sz);
198
204
  } tree_iterator;
199
205
 
@@ -267,9 +273,28 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
267
273
  ((tree_iterator *)p)->strncomp);
268
274
  }
269
275
 
276
+ static bool tree_iterator__move_to_next(
277
+ tree_iterator *ti, tree_iterator_frame *tf)
278
+ {
279
+ if (tf->next > tf->current + 1)
280
+ ti->path_ambiguities--;
281
+
282
+ if (!tf->up) { /* at root */
283
+ tf->current = tf->next;
284
+ return false;
285
+ }
286
+
287
+ for (; tf->current < tf->next; tf->current++) {
288
+ git_tree_free(tf->entries[tf->current]->tree);
289
+ tf->entries[tf->current]->tree = NULL;
290
+ }
291
+
292
+ return (tf->current < tf->n_entries);
293
+ }
294
+
270
295
  static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
271
296
  {
272
- int error;
297
+ int error = 0;
273
298
  const git_tree_entry *te, *last = NULL;
274
299
 
275
300
  tf->next = tf->current;
@@ -280,18 +305,23 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
280
305
  if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
281
306
  break;
282
307
 
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;
308
+ /* try to load trees for items in [current,next) range */
309
+ if (!error && git_tree_entry__is_tree(te))
310
+ error = git_tree_lookup(
311
+ &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
288
312
  }
289
313
 
290
314
  if (tf->next > tf->current + 1)
291
315
  ti->path_ambiguities++;
292
316
 
317
+ /* if a tree lookup failed, advance over this span and return failure */
318
+ if (error < 0) {
319
+ tree_iterator__move_to_next(ti, tf);
320
+ return error;
321
+ }
322
+
293
323
  if (last && !tree_iterator__current_filename(ti, last))
294
- return -1;
324
+ return -1; /* must have been allocation failure */
295
325
 
296
326
  return 0;
297
327
  }
@@ -309,7 +339,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
309
339
  size_t i, n_entries = 0;
310
340
 
311
341
  if (head->current >= head->n_entries || !head->entries[head->current]->tree)
312
- return 0;
342
+ return GIT_ITEROVER;
313
343
 
314
344
  for (i = head->current; i < head->next; ++i)
315
345
  n_entries += git_tree_entrycount(head->entries[i]->tree);
@@ -360,7 +390,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
360
390
  }
361
391
  }
362
392
 
363
- ti->path_has_filename = false;
393
+ ti->path_has_filename = ti->entry_is_current = false;
364
394
 
365
395
  if ((error = tree_iterator__set_next(ti, tf)) < 0)
366
396
  return error;
@@ -372,25 +402,6 @@ static int tree_iterator__push_frame(tree_iterator *ti)
372
402
  return 0;
373
403
  }
374
404
 
375
- static bool tree_iterator__move_to_next(
376
- tree_iterator *ti, tree_iterator_frame *tf)
377
- {
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);
392
- }
393
-
394
405
  static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
395
406
  {
396
407
  tree_iterator_frame *tf = ti->head;
@@ -413,7 +424,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
413
424
  return true;
414
425
  }
415
426
 
416
- static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
427
+ static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
417
428
  {
418
429
  while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
419
430
 
@@ -422,22 +433,18 @@ static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
422
433
  ti->path_ambiguities = 0;
423
434
  git_buf_clear(&ti->path);
424
435
  }
425
-
426
- return 0;
427
436
  }
428
437
 
429
- static int tree_iterator__current(
430
- const git_index_entry **entry, git_iterator *self)
438
+ static int tree_iterator__update_entry(tree_iterator *ti)
431
439
  {
432
- tree_iterator *ti = (tree_iterator *)self;
433
- tree_iterator_frame *tf = ti->head;
434
- const git_tree_entry *te;
440
+ tree_iterator_frame *tf;
441
+ const git_tree_entry *te;
435
442
 
436
- iterator__clear_entry(entry);
443
+ if (ti->entry_is_current)
444
+ return 0;
437
445
 
438
- if (tf->current >= tf->n_entries)
439
- return 0;
440
- te = tf->entries[tf->current]->te;
446
+ tf = ti->head;
447
+ te = tf->entries[tf->current]->te;
441
448
 
442
449
  ti->entry.mode = te->attr;
443
450
  git_oid_cpy(&ti->entry.oid, &te->oid);
@@ -448,12 +455,36 @@ static int tree_iterator__current(
448
455
  if (ti->path_ambiguities > 0)
449
456
  tree_iterator__rewrite_filename(ti);
450
457
 
451
- if (iterator__past_end(ti, ti->entry.path))
452
- return tree_iterator__pop_all(ti, true, false);
458
+ if (iterator__past_end(ti, ti->entry.path)) {
459
+ tree_iterator__pop_all(ti, true, false);
460
+ return GIT_ITEROVER;
461
+ }
462
+
463
+ ti->entry_is_current = true;
464
+
465
+ return 0;
466
+ }
467
+
468
+ static int tree_iterator__current(
469
+ const git_index_entry **entry, git_iterator *self)
470
+ {
471
+ int error;
472
+ tree_iterator *ti = (tree_iterator *)self;
473
+ tree_iterator_frame *tf = ti->head;
474
+
475
+ iterator__clear_entry(entry);
476
+
477
+ if (tf->current >= tf->n_entries)
478
+ return GIT_ITEROVER;
479
+
480
+ if ((error = tree_iterator__update_entry(ti)) < 0)
481
+ return error;
453
482
 
454
483
  if (entry)
455
484
  *entry = &ti->entry;
456
485
 
486
+ ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
487
+
457
488
  return 0;
458
489
  }
459
490
 
@@ -465,8 +496,10 @@ static int tree_iterator__advance_into(
465
496
 
466
497
  iterator__clear_entry(entry);
467
498
 
468
- if (tree_iterator__at_tree(ti) &&
469
- !(error = tree_iterator__push_frame(ti)))
499
+ if (tree_iterator__at_tree(ti))
500
+ error = tree_iterator__push_frame(ti);
501
+
502
+ if (!error && entry)
470
503
  error = tree_iterator__current(entry, self);
471
504
 
472
505
  return error;
@@ -481,8 +514,11 @@ static int tree_iterator__advance(
481
514
 
482
515
  iterator__clear_entry(entry);
483
516
 
484
- if (tf->current > tf->n_entries)
485
- return 0;
517
+ if (tf->current >= tf->n_entries)
518
+ return GIT_ITEROVER;
519
+
520
+ if (!iterator__has_been_accessed(ti))
521
+ return tree_iterator__current(entry, self);
486
522
 
487
523
  if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
488
524
  tree_iterator__at_tree(ti))
@@ -490,7 +526,7 @@ static int tree_iterator__advance(
490
526
 
491
527
  if (ti->path_has_filename) {
492
528
  git_buf_rtruncate_at_char(&ti->path, '/');
493
- ti->path_has_filename = false;
529
+ ti->path_has_filename = ti->entry_is_current = false;
494
530
  }
495
531
 
496
532
  /* scan forward and up, advancing in frame or popping frame when done */
@@ -581,6 +617,9 @@ int git_iterator_for_tree(
581
617
  if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
582
618
  return error;
583
619
 
620
+ ti = git__calloc(1, sizeof(tree_iterator));
621
+ GITERR_CHECK_ALLOC(ti);
622
+
584
623
  ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
585
624
 
586
625
  if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
@@ -697,7 +736,9 @@ static int index_iterator__current(
697
736
  if (entry)
698
737
  *entry = ie;
699
738
 
700
- return 0;
739
+ ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
740
+
741
+ return (ie != NULL) ? 0 : GIT_ITEROVER;
701
742
  }
702
743
 
703
744
  static int index_iterator__at_end(git_iterator *self)
@@ -713,6 +754,9 @@ static int index_iterator__advance(
713
754
  size_t entrycount = git_index_entrycount(ii->index);
714
755
  const git_index_entry *ie;
715
756
 
757
+ if (!iterator__has_been_accessed(ii))
758
+ return index_iterator__current(entry, self);
759
+
716
760
  if (index_iterator__at_tree(ii)) {
717
761
  if (iterator__do_autoexpand(ii)) {
718
762
  ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
@@ -810,7 +854,8 @@ int git_iterator_for_index(
810
854
  const char *start,
811
855
  const char *end)
812
856
  {
813
- index_iterator *ii;
857
+ index_iterator *ii = git__calloc(1, sizeof(index_iterator));
858
+ GITERR_CHECK_ALLOC(ii);
814
859
 
815
860
  ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
816
861
 
@@ -833,237 +878,240 @@ int git_iterator_for_index(
833
878
  }
834
879
 
835
880
 
836
- #define WORKDIR_MAX_DEPTH 100
837
-
838
- typedef struct workdir_iterator_frame workdir_iterator_frame;
839
- struct workdir_iterator_frame {
840
- workdir_iterator_frame *next;
881
+ typedef struct fs_iterator_frame fs_iterator_frame;
882
+ struct fs_iterator_frame {
883
+ fs_iterator_frame *next;
841
884
  git_vector entries;
842
885
  size_t index;
843
886
  };
844
887
 
845
- typedef struct {
888
+ typedef struct fs_iterator fs_iterator;
889
+ struct fs_iterator {
846
890
  git_iterator base;
847
891
  git_iterator_callbacks cb;
848
- workdir_iterator_frame *stack;
849
- git_ignores ignores;
892
+ fs_iterator_frame *stack;
850
893
  git_index_entry entry;
851
894
  git_buf path;
852
895
  size_t root_len;
853
- int is_ignored;
854
896
  int depth;
855
- } workdir_iterator;
856
897
 
857
- GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
858
- {
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
- }
876
- }
898
+ int (*enter_dir_cb)(fs_iterator *self);
899
+ int (*leave_dir_cb)(fs_iterator *self);
900
+ int (*update_entry_cb)(fs_iterator *self);
901
+ };
902
+
903
+ #define FS_MAX_DEPTH 100
877
904
 
878
- static workdir_iterator_frame *workdir_iterator__alloc_frame(
879
- workdir_iterator *wi)
905
+ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
880
906
  {
881
- workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
907
+ fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
882
908
  git_vector_cmp entry_compare = CASESELECT(
883
- iterator__ignore_case(wi),
909
+ iterator__ignore_case(fi),
884
910
  git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
885
911
 
886
- if (wf == NULL)
887
- return NULL;
888
-
889
- if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
890
- git__free(wf);
891
- return NULL;
912
+ if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
913
+ git__free(ff);
914
+ ff = NULL;
892
915
  }
893
916
 
894
- return wf;
917
+ return ff;
895
918
  }
896
919
 
897
- static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
920
+ static void fs_iterator__free_frame(fs_iterator_frame *ff)
898
921
  {
899
- unsigned int i;
922
+ size_t i;
900
923
  git_path_with_stat *path;
901
924
 
902
- git_vector_foreach(&wf->entries, i, path)
925
+ git_vector_foreach(&ff->entries, i, path)
903
926
  git__free(path);
904
- git_vector_free(&wf->entries);
905
- git__free(wf);
927
+ git_vector_free(&ff->entries);
928
+ git__free(ff);
929
+ }
930
+
931
+ static void fs_iterator__pop_frame(
932
+ fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
933
+ {
934
+ if (fi && fi->stack == ff) {
935
+ if (!ff->next && !pop_last) {
936
+ memset(&fi->entry, 0, sizeof(fi->entry));
937
+ return;
938
+ }
939
+
940
+ if (fi->leave_dir_cb)
941
+ (void)fi->leave_dir_cb(fi);
942
+
943
+ fi->stack = ff->next;
944
+ fi->depth--;
945
+ }
946
+
947
+ fs_iterator__free_frame(ff);
906
948
  }
907
949
 
908
- static int workdir_iterator__update_entry(workdir_iterator *wi);
950
+ static int fs_iterator__update_entry(fs_iterator *fi);
951
+ static int fs_iterator__advance_over(
952
+ const git_index_entry **entry, git_iterator *self);
909
953
 
910
- static int workdir_iterator__entry_cmp(const void *i, const void *item)
954
+ static int fs_iterator__entry_cmp(const void *i, const void *item)
911
955
  {
912
- const workdir_iterator *wi = (const workdir_iterator *)i;
956
+ const fs_iterator *fi = (const fs_iterator *)i;
913
957
  const git_path_with_stat *ps = item;
914
- return wi->base.prefixcomp(wi->base.start, ps->path);
958
+ return fi->base.prefixcomp(fi->base.start, ps->path);
915
959
  }
916
960
 
917
- static void workdir_iterator__seek_frame_start(
918
- workdir_iterator *wi, workdir_iterator_frame *wf)
961
+ static void fs_iterator__seek_frame_start(
962
+ fs_iterator *fi, fs_iterator_frame *ff)
919
963
  {
920
- if (!wf)
964
+ if (!ff)
921
965
  return;
922
966
 
923
- if (wi->base.start)
967
+ if (fi->base.start)
924
968
  git_vector_bsearch2(
925
- &wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
969
+ &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
926
970
  else
927
- wf->index = 0;
928
-
929
- if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
930
- wf->index++;
971
+ ff->index = 0;
931
972
  }
932
973
 
933
- static int workdir_iterator__expand_dir(workdir_iterator *wi)
974
+ static int fs_iterator__expand_dir(fs_iterator *fi)
934
975
  {
935
976
  int error;
936
- workdir_iterator_frame *wf;
977
+ fs_iterator_frame *ff;
978
+
979
+ if (fi->depth > FS_MAX_DEPTH) {
980
+ giterr_set(GITERR_REPOSITORY,
981
+ "Directory nesting is too deep (%d)", fi->depth);
982
+ return -1;
983
+ }
937
984
 
938
- wf = workdir_iterator__alloc_frame(wi);
939
- GITERR_CHECK_ALLOC(wf);
985
+ ff = fs_iterator__alloc_frame(fi);
986
+ GITERR_CHECK_ALLOC(ff);
940
987
 
941
988
  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);
989
+ fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
990
+ fi->base.start, fi->base.end, &ff->entries);
944
991
 
945
- if (error < 0 || wf->entries.length == 0) {
946
- workdir_iterator__free_frame(wf);
947
- return GIT_ENOTFOUND;
992
+ if (error < 0) {
993
+ fs_iterator__free_frame(ff);
994
+ fs_iterator__advance_over(NULL, (git_iterator *)fi);
995
+ return error;
948
996
  }
949
997
 
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;
998
+ if (ff->entries.length == 0) {
999
+ fs_iterator__free_frame(ff);
1000
+ return GIT_ENOTFOUND;
955
1001
  }
956
1002
 
957
- workdir_iterator__seek_frame_start(wi, wf);
1003
+ fs_iterator__seek_frame_start(fi, ff);
958
1004
 
959
- /* only push new ignores if this is not top level directory */
960
- if (wi->stack != NULL) {
961
- ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
962
- (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
963
- }
1005
+ ff->next = fi->stack;
1006
+ fi->stack = ff;
1007
+ fi->depth++;
964
1008
 
965
- wf->next = wi->stack;
966
- wi->stack = wf;
1009
+ if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1010
+ return error;
967
1011
 
968
- return workdir_iterator__update_entry(wi);
1012
+ return fs_iterator__update_entry(fi);
969
1013
  }
970
1014
 
971
- static int workdir_iterator__current(
1015
+ static int fs_iterator__current(
972
1016
  const git_index_entry **entry, git_iterator *self)
973
1017
  {
974
- workdir_iterator *wi = (workdir_iterator *)self;
1018
+ fs_iterator *fi = (fs_iterator *)self;
1019
+ const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1020
+
975
1021
  if (entry)
976
- *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
977
- return 0;
1022
+ *entry = fe;
1023
+
1024
+ fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1025
+
1026
+ return (fe != NULL) ? 0 : GIT_ITEROVER;
978
1027
  }
979
1028
 
980
- static int workdir_iterator__at_end(git_iterator *self)
1029
+ static int fs_iterator__at_end(git_iterator *self)
981
1030
  {
982
- return (((workdir_iterator *)self)->entry.path == NULL);
1031
+ return (((fs_iterator *)self)->entry.path == NULL);
983
1032
  }
984
1033
 
985
- static int workdir_iterator__advance_into(
1034
+ static int fs_iterator__advance_into(
986
1035
  const git_index_entry **entry, git_iterator *iter)
987
1036
  {
988
1037
  int error = 0;
989
- workdir_iterator *wi = (workdir_iterator *)iter;
1038
+ fs_iterator *fi = (fs_iterator *)iter;
990
1039
 
991
1040
  iterator__clear_entry(entry);
992
1041
 
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
1042
+ /* Allow you to explicitly advance into a commit/submodule (as well as a
1043
+ * tree) to avoid cases where an entry is mislabeled as a submodule in
1044
+ * the working directory. The fs iterator will never have COMMMIT
1045
+ * entries on it's own, but a wrapper might add them.
996
1046
  */
997
- if (wi->entry.path != NULL &&
998
- (wi->entry.mode == GIT_FILEMODE_TREE ||
999
- wi->entry.mode == GIT_FILEMODE_COMMIT))
1047
+ if (fi->entry.path != NULL &&
1048
+ (fi->entry.mode == GIT_FILEMODE_TREE ||
1049
+ fi->entry.mode == GIT_FILEMODE_COMMIT))
1000
1050
  /* returns GIT_ENOTFOUND if the directory is empty */
1001
- error = workdir_iterator__expand_dir(wi);
1051
+ error = fs_iterator__expand_dir(fi);
1002
1052
 
1003
1053
  if (!error && entry)
1004
- error = workdir_iterator__current(entry, iter);
1054
+ error = fs_iterator__current(entry, iter);
1055
+
1056
+ if (!error && !fi->entry.path)
1057
+ error = GIT_ITEROVER;
1005
1058
 
1006
1059
  return error;
1007
1060
  }
1008
1061
 
1009
- static int workdir_iterator__advance(
1062
+ static int fs_iterator__advance_over(
1010
1063
  const git_index_entry **entry, git_iterator *self)
1011
1064
  {
1012
1065
  int error = 0;
1013
- workdir_iterator *wi = (workdir_iterator *)self;
1014
- workdir_iterator_frame *wf;
1066
+ fs_iterator *fi = (fs_iterator *)self;
1067
+ fs_iterator_frame *ff;
1015
1068
  git_path_with_stat *next;
1016
1069
 
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
-
1031
1070
  if (entry != NULL)
1032
1071
  *entry = NULL;
1033
1072
 
1034
- while (wi->entry.path != NULL) {
1035
- wf = wi->stack;
1036
- next = git_vector_get(&wf->entries, ++wf->index);
1073
+ while (fi->entry.path != NULL) {
1074
+ ff = fi->stack;
1075
+ next = git_vector_get(&ff->entries, ++ff->index);
1037
1076
 
1038
- if (next != NULL) {
1039
- /* match git's behavior of ignoring anything named ".git" */
1040
- if (path_is_dotgit(next))
1041
- continue;
1042
- /* else found a good entry */
1077
+ if (next != NULL)
1043
1078
  break;
1044
- }
1045
1079
 
1046
- /* pop stack if anything is left to pop */
1047
- if (!wf->next) {
1048
- memset(&wi->entry, 0, sizeof(wi->entry));
1049
- return 0;
1050
- }
1051
-
1052
- wi->stack = wf->next;
1053
- wi->depth--;
1054
- workdir_iterator__free_frame(wf);
1055
- git_ignore__pop_dir(&wi->ignores);
1080
+ fs_iterator__pop_frame(fi, ff, false);
1056
1081
  }
1057
1082
 
1058
- error = workdir_iterator__update_entry(wi);
1083
+ error = fs_iterator__update_entry(fi);
1059
1084
 
1060
1085
  if (!error && entry != NULL)
1061
- error = workdir_iterator__current(entry, self);
1086
+ error = fs_iterator__current(entry, self);
1062
1087
 
1063
1088
  return error;
1064
1089
  }
1065
1090
 
1066
- static int workdir_iterator__seek(git_iterator *self, const char *prefix)
1091
+ static int fs_iterator__advance(
1092
+ const git_index_entry **entry, git_iterator *self)
1093
+ {
1094
+ fs_iterator *fi = (fs_iterator *)self;
1095
+
1096
+ if (!iterator__has_been_accessed(fi))
1097
+ return fs_iterator__current(entry, self);
1098
+
1099
+ /* given include_trees & autoexpand, we might have to go into a tree */
1100
+ if (iterator__do_autoexpand(fi) &&
1101
+ fi->entry.path != NULL &&
1102
+ fi->entry.mode == GIT_FILEMODE_TREE)
1103
+ {
1104
+ int error = fs_iterator__advance_into(entry, self);
1105
+ if (error != GIT_ENOTFOUND)
1106
+ return error;
1107
+ /* continue silently past empty directories if autoexpanding */
1108
+ giterr_clear();
1109
+ }
1110
+
1111
+ return fs_iterator__advance_over(entry, self);
1112
+ }
1113
+
1114
+ static int fs_iterator__seek(git_iterator *self, const char *prefix)
1067
1115
  {
1068
1116
  GIT_UNUSED(self);
1069
1117
  GIT_UNUSED(prefix);
@@ -1073,108 +1121,210 @@ static int workdir_iterator__seek(git_iterator *self, const char *prefix)
1073
1121
  return 0;
1074
1122
  }
1075
1123
 
1076
- static int workdir_iterator__reset(
1124
+ static int fs_iterator__reset(
1077
1125
  git_iterator *self, const char *start, const char *end)
1078
1126
  {
1079
- workdir_iterator *wi = (workdir_iterator *)self;
1127
+ int error;
1128
+ fs_iterator *fi = (fs_iterator *)self;
1080
1129
 
1081
- while (wi->stack != NULL && wi->stack->next != NULL) {
1082
- workdir_iterator_frame *wf = wi->stack;
1083
- wi->stack = wf->next;
1084
- workdir_iterator__free_frame(wf);
1085
- git_ignore__pop_dir(&wi->ignores);
1086
- }
1087
- wi->depth = 0;
1130
+ while (fi->stack != NULL && fi->stack->next != NULL)
1131
+ fs_iterator__pop_frame(fi, fi->stack, false);
1132
+ fi->depth = 0;
1088
1133
 
1089
- if (iterator__reset_range(self, start, end) < 0)
1090
- return -1;
1134
+ if ((error = iterator__reset_range(self, start, end)) < 0)
1135
+ return error;
1136
+
1137
+ fs_iterator__seek_frame_start(fi, fi->stack);
1091
1138
 
1092
- workdir_iterator__seek_frame_start(wi, wi->stack);
1139
+ error = fs_iterator__update_entry(fi);
1140
+ if (error == GIT_ITEROVER)
1141
+ error = 0;
1093
1142
 
1094
- return workdir_iterator__update_entry(wi);
1143
+ return error;
1095
1144
  }
1096
1145
 
1097
- static void workdir_iterator__free(git_iterator *self)
1146
+ static void fs_iterator__free(git_iterator *self)
1098
1147
  {
1099
- workdir_iterator *wi = (workdir_iterator *)self;
1148
+ fs_iterator *fi = (fs_iterator *)self;
1100
1149
 
1101
- while (wi->stack != NULL) {
1102
- workdir_iterator_frame *wf = wi->stack;
1103
- wi->stack = wf->next;
1104
- workdir_iterator__free_frame(wf);
1105
- }
1150
+ while (fi->stack != NULL)
1151
+ fs_iterator__pop_frame(fi, fi->stack, true);
1106
1152
 
1107
- git_ignore__free(&wi->ignores);
1108
- git_buf_free(&wi->path);
1153
+ git_buf_free(&fi->path);
1109
1154
  }
1110
1155
 
1111
- static int workdir_iterator__update_entry(workdir_iterator *wi)
1156
+ static int fs_iterator__update_entry(fs_iterator *fi)
1112
1157
  {
1113
- int error = 0;
1114
- git_path_with_stat *ps =
1115
- git_vector_get(&wi->stack->entries, wi->stack->index);
1158
+ git_path_with_stat *ps;
1116
1159
 
1117
- git_buf_truncate(&wi->path, wi->root_len);
1118
- memset(&wi->entry, 0, sizeof(wi->entry));
1160
+ memset(&fi->entry, 0, sizeof(fi->entry));
1119
1161
 
1162
+ if (!fi->stack)
1163
+ return GIT_ITEROVER;
1164
+
1165
+ ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1120
1166
  if (!ps)
1121
- return 0;
1167
+ return GIT_ITEROVER;
1122
1168
 
1123
- /* skip over .git entries */
1124
- if (path_is_dotgit(ps))
1125
- return workdir_iterator__advance(NULL, (git_iterator *)wi);
1169
+ git_buf_truncate(&fi->path, fi->root_len);
1170
+ if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1171
+ return -1;
1126
1172
 
1127
- if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
1173
+ if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1174
+ return GIT_ITEROVER;
1175
+
1176
+ fi->entry.path = ps->path;
1177
+ git_index_entry__init_from_stat(&fi->entry, &ps->st);
1178
+
1179
+ /* need different mode here to keep directories during iteration */
1180
+ fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1181
+
1182
+ /* allow wrapper to check/update the entry (can force skip) */
1183
+ if (fi->update_entry_cb &&
1184
+ fi->update_entry_cb(fi) == GIT_ENOTFOUND)
1185
+ return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1186
+
1187
+ /* if this is a tree and trees aren't included, then skip */
1188
+ if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1189
+ int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
1190
+ if (error != GIT_ENOTFOUND)
1191
+ return error;
1192
+ giterr_clear();
1193
+ return fs_iterator__advance_over(NULL, (git_iterator *)fi);
1194
+ }
1195
+
1196
+ return 0;
1197
+ }
1198
+
1199
+ static int fs_iterator__initialize(
1200
+ git_iterator **out, fs_iterator *fi, const char *root)
1201
+ {
1202
+ int error;
1203
+
1204
+ if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1205
+ git__free(fi);
1128
1206
  return -1;
1207
+ }
1208
+ fi->root_len = fi->path.size;
1129
1209
 
1130
- if (iterator__past_end(wi, wi->path.ptr + wi->root_len))
1131
- return 0;
1210
+ if ((error = fs_iterator__expand_dir(fi)) < 0) {
1211
+ if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1212
+ giterr_clear();
1213
+ error = 0;
1214
+ } else {
1215
+ git_iterator_free((git_iterator *)fi);
1216
+ fi = NULL;
1217
+ }
1218
+ }
1132
1219
 
1133
- wi->entry.path = ps->path;
1220
+ *out = (git_iterator *)fi;
1221
+ return error;
1222
+ }
1134
1223
 
1135
- wi->is_ignored = -1;
1224
+ int git_iterator_for_filesystem(
1225
+ git_iterator **out,
1226
+ const char *root,
1227
+ git_iterator_flag_t flags,
1228
+ const char *start,
1229
+ const char *end)
1230
+ {
1231
+ fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1232
+ GITERR_CHECK_ALLOC(fi);
1136
1233
 
1137
- git_index_entry__init_from_stat(&wi->entry, &ps->st);
1234
+ ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1138
1235
 
1139
- /* need different mode here to keep directories during iteration */
1140
- wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1236
+ if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1237
+ fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1141
1238
 
1142
- /* if this is a file type we don't handle, treat as ignored */
1143
- if (wi->entry.mode == 0) {
1144
- wi->is_ignored = 1;
1145
- return 0;
1239
+ return fs_iterator__initialize(out, fi, root);
1240
+ }
1241
+
1242
+
1243
+ typedef struct {
1244
+ fs_iterator fi;
1245
+ git_ignores ignores;
1246
+ int is_ignored;
1247
+ } workdir_iterator;
1248
+
1249
+ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1250
+ {
1251
+ size_t len;
1252
+
1253
+ if (!path || (len = path->size) < 4)
1254
+ return false;
1255
+
1256
+ if (path->ptr[len - 1] == '/')
1257
+ len--;
1258
+
1259
+ if (tolower(path->ptr[len - 1]) != 't' ||
1260
+ tolower(path->ptr[len - 2]) != 'i' ||
1261
+ tolower(path->ptr[len - 3]) != 'g' ||
1262
+ tolower(path->ptr[len - 4]) != '.')
1263
+ return false;
1264
+
1265
+ return (len == 4 || path->ptr[len - 5] == '/');
1266
+ }
1267
+
1268
+ static int workdir_iterator__enter_dir(fs_iterator *fi)
1269
+ {
1270
+ /* only push new ignores if this is not top level directory */
1271
+ if (fi->stack->next != NULL) {
1272
+ workdir_iterator *wi = (workdir_iterator *)fi;
1273
+ ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1274
+
1275
+ (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1146
1276
  }
1147
1277
 
1148
- /* if this isn't a tree, then we're done */
1149
- if (wi->entry.mode != GIT_FILEMODE_TREE)
1150
- return 0;
1278
+ return 0;
1279
+ }
1151
1280
 
1152
- /* detect submodules */
1153
- error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path);
1154
- if (error == GIT_ENOTFOUND)
1155
- giterr_clear();
1281
+ static int workdir_iterator__leave_dir(fs_iterator *fi)
1282
+ {
1283
+ workdir_iterator *wi = (workdir_iterator *)fi;
1284
+ git_ignore__pop_dir(&wi->ignores);
1285
+ return 0;
1286
+ }
1156
1287
 
1157
- if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */
1158
- error = 0;
1288
+ static int workdir_iterator__update_entry(fs_iterator *fi)
1289
+ {
1290
+ int error = 0;
1291
+ workdir_iterator *wi = (workdir_iterator *)fi;
1159
1292
 
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;
1293
+ /* skip over .git entries */
1294
+ if (workdir_path_is_dotgit(&fi->path))
1295
+ return GIT_ENOTFOUND;
1296
+
1297
+ /* reset is_ignored since we haven't checked yet */
1298
+ wi->is_ignored = -1;
1299
+
1300
+ /* check if apparent tree entries are actually submodules */
1301
+ if (fi->entry.mode != GIT_FILEMODE_TREE)
1166
1302
  return 0;
1303
+
1304
+ error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
1305
+ if (error < 0)
1306
+ giterr_clear();
1307
+
1308
+ /* mark submodule (or any dir with .git) as GITLINK and remove slash */
1309
+ if (!error || error == GIT_EEXISTS) {
1310
+ fi->entry.mode = S_IFGITLINK;
1311
+ fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
1167
1312
  }
1168
1313
 
1169
- if (iterator__include_trees(wi))
1170
- return 0;
1314
+ return 0;
1315
+ }
1171
1316
 
1172
- return workdir_iterator__advance(NULL, (git_iterator *)wi);
1317
+ static void workdir_iterator__free(git_iterator *self)
1318
+ {
1319
+ workdir_iterator *wi = (workdir_iterator *)self;
1320
+ fs_iterator__free(self);
1321
+ git_ignore__free(&wi->ignores);
1173
1322
  }
1174
1323
 
1175
- int git_iterator_for_workdir(
1176
- git_iterator **iter,
1324
+ int git_iterator_for_workdir_ext(
1325
+ git_iterator **out,
1177
1326
  git_repository *repo,
1327
+ const char *repo_workdir,
1178
1328
  git_iterator_flag_t flags,
1179
1329
  const char *start,
1180
1330
  const char *end)
@@ -1182,38 +1332,31 @@ int git_iterator_for_workdir(
1182
1332
  int error;
1183
1333
  workdir_iterator *wi;
1184
1334
 
1185
- assert(iter && repo);
1186
-
1187
- if ((error = git_repository__ensure_not_bare(
1188
- repo, "scan working directory")) < 0)
1189
- return error;
1335
+ if (!repo_workdir) {
1336
+ if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1337
+ return GIT_EBAREREPO;
1338
+ repo_workdir = git_repository_workdir(repo);
1339
+ }
1190
1340
 
1191
- ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
1341
+ /* initialize as an fs iterator then do overrides */
1342
+ wi = git__calloc(1, sizeof(workdir_iterator));
1343
+ GITERR_CHECK_ALLOC(wi);
1344
+ ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1192
1345
 
1193
- if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
1194
- goto fail;
1346
+ wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1347
+ wi->fi.cb.free = workdir_iterator__free;
1348
+ wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1349
+ wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1350
+ wi->fi.update_entry_cb = workdir_iterator__update_entry;
1195
1351
 
1196
- if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
1197
- git_path_to_dir(&wi->path) < 0 ||
1198
- git_ignore__for_path(repo, "", &wi->ignores) < 0)
1352
+ if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
1353
+ (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
1199
1354
  {
1200
- git__free(wi);
1201
- return -1;
1202
- }
1203
- wi->root_len = wi->path.size;
1204
-
1205
- if ((error = workdir_iterator__expand_dir(wi)) < 0) {
1206
- if (error != GIT_ENOTFOUND)
1207
- goto fail;
1208
- giterr_clear();
1355
+ git_iterator_free((git_iterator *)wi);
1356
+ return error;
1209
1357
  }
1210
1358
 
1211
- *iter = (git_iterator *)wi;
1212
- return 0;
1213
-
1214
- fail:
1215
- git_iterator_free((git_iterator *)wi);
1216
- return error;
1359
+ return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1217
1360
  }
1218
1361
 
1219
1362
 
@@ -1315,7 +1458,8 @@ bool git_iterator_current_is_ignored(git_iterator *iter)
1315
1458
  if (wi->is_ignored != -1)
1316
1459
  return (bool)(wi->is_ignored != 0);
1317
1460
 
1318
- if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
1461
+ if (git_ignore__lookup(
1462
+ &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
1319
1463
  wi->is_ignored = true;
1320
1464
 
1321
1465
  return (bool)wi->is_ignored;
@@ -1340,10 +1484,10 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
1340
1484
  {
1341
1485
  workdir_iterator *wi = (workdir_iterator *)iter;
1342
1486
 
1343
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path)
1487
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
1344
1488
  *path = NULL;
1345
1489
  else
1346
- *path = &wi->path;
1490
+ *path = &wi->fi.path;
1347
1491
 
1348
1492
  return 0;
1349
1493
  }