rugged 0.25.0b2 → 0.25.0b3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/ext/rugged/extconf.rb +3 -1
  4. data/ext/rugged/rugged.c +1 -1
  5. data/ext/rugged/rugged.h +1 -1
  6. data/ext/rugged/rugged_blob.c +29 -38
  7. data/ext/rugged/rugged_commit.c +215 -78
  8. data/ext/rugged/rugged_rebase.c +18 -11
  9. data/ext/rugged/rugged_remote.c +2 -2
  10. data/ext/rugged/rugged_tree.c +132 -0
  11. data/lib/rugged/version.rb +1 -1
  12. data/vendor/libgit2/CMakeLists.txt +11 -3
  13. data/vendor/libgit2/include/git2.h +1 -0
  14. data/vendor/libgit2/include/git2/blob.h +39 -28
  15. data/vendor/libgit2/include/git2/commit.h +30 -0
  16. data/vendor/libgit2/include/git2/common.h +16 -1
  17. data/vendor/libgit2/include/git2/merge.h +10 -1
  18. data/vendor/libgit2/include/git2/proxy.h +92 -0
  19. data/vendor/libgit2/include/git2/refs.h +11 -0
  20. data/vendor/libgit2/include/git2/remote.h +17 -4
  21. data/vendor/libgit2/include/git2/signature.h +13 -0
  22. data/vendor/libgit2/include/git2/sys/merge.h +177 -0
  23. data/vendor/libgit2/include/git2/sys/remote.h +16 -0
  24. data/vendor/libgit2/include/git2/sys/stream.h +2 -1
  25. data/vendor/libgit2/include/git2/sys/transport.h +3 -1
  26. data/vendor/libgit2/include/git2/tag.h +9 -0
  27. data/vendor/libgit2/include/git2/tree.h +55 -0
  28. data/vendor/libgit2/src/annotated_commit.c +99 -80
  29. data/vendor/libgit2/src/annotated_commit.h +5 -2
  30. data/vendor/libgit2/src/array.h +40 -0
  31. data/vendor/libgit2/src/blame.c +8 -3
  32. data/vendor/libgit2/src/blame_git.c +2 -1
  33. data/vendor/libgit2/src/blob.c +71 -39
  34. data/vendor/libgit2/src/branch.c +2 -1
  35. data/vendor/libgit2/src/checkout.c +66 -42
  36. data/vendor/libgit2/src/commit.c +67 -3
  37. data/vendor/libgit2/src/config_cache.c +2 -1
  38. data/vendor/libgit2/src/config_file.c +32 -27
  39. data/vendor/libgit2/src/curl_stream.c +89 -6
  40. data/vendor/libgit2/src/delta-apply.c +36 -5
  41. data/vendor/libgit2/src/delta-apply.h +12 -0
  42. data/vendor/libgit2/src/describe.c +3 -2
  43. data/vendor/libgit2/src/diff.c +13 -20
  44. data/vendor/libgit2/src/diff_tform.c +5 -3
  45. data/vendor/libgit2/src/filebuf.c +12 -2
  46. data/vendor/libgit2/src/filebuf.h +1 -0
  47. data/vendor/libgit2/src/fnmatch.c +18 -5
  48. data/vendor/libgit2/src/global.c +18 -0
  49. data/vendor/libgit2/src/global.h +1 -0
  50. data/vendor/libgit2/src/ignore.c +11 -3
  51. data/vendor/libgit2/src/index.c +11 -5
  52. data/vendor/libgit2/src/indexer.c +11 -7
  53. data/vendor/libgit2/src/iterator.c +1575 -1468
  54. data/vendor/libgit2/src/iterator.h +52 -69
  55. data/vendor/libgit2/src/merge.c +160 -63
  56. data/vendor/libgit2/src/merge.h +61 -2
  57. data/vendor/libgit2/src/merge_driver.c +397 -0
  58. data/vendor/libgit2/src/merge_driver.h +60 -0
  59. data/vendor/libgit2/src/merge_file.c +11 -49
  60. data/vendor/libgit2/src/netops.c +12 -10
  61. data/vendor/libgit2/src/object.c +3 -6
  62. data/vendor/libgit2/src/object_api.c +19 -1
  63. data/vendor/libgit2/src/odb_loose.c +1 -1
  64. data/vendor/libgit2/src/openssl_stream.c +16 -3
  65. data/vendor/libgit2/src/pack-objects.c +3 -1
  66. data/vendor/libgit2/src/pack.c +5 -9
  67. data/vendor/libgit2/src/path.c +14 -0
  68. data/vendor/libgit2/src/path.h +12 -0
  69. data/vendor/libgit2/src/pathspec.c +1 -1
  70. data/vendor/libgit2/src/posix.c +7 -0
  71. data/vendor/libgit2/src/posix.h +1 -0
  72. data/vendor/libgit2/src/proxy.c +32 -0
  73. data/vendor/libgit2/src/proxy.h +14 -0
  74. data/vendor/libgit2/src/push.c +7 -7
  75. data/vendor/libgit2/src/rebase.c +61 -31
  76. data/vendor/libgit2/src/refdb_fs.c +1 -0
  77. data/vendor/libgit2/src/refs.c +16 -1
  78. data/vendor/libgit2/src/remote.c +20 -6
  79. data/vendor/libgit2/src/repository.c +1 -1
  80. data/vendor/libgit2/src/reset.c +1 -1
  81. data/vendor/libgit2/src/settings.c +23 -1
  82. data/vendor/libgit2/src/signature.c +26 -1
  83. data/vendor/libgit2/src/stransport_stream.c +5 -2
  84. data/vendor/libgit2/src/stream.h +2 -2
  85. data/vendor/libgit2/src/submodule.c +3 -2
  86. data/vendor/libgit2/src/tag.c +8 -2
  87. data/vendor/libgit2/src/transports/http.c +32 -9
  88. data/vendor/libgit2/src/transports/local.c +4 -1
  89. data/vendor/libgit2/src/transports/smart.c +6 -0
  90. data/vendor/libgit2/src/transports/smart.h +1 -0
  91. data/vendor/libgit2/src/transports/smart_protocol.c +61 -17
  92. data/vendor/libgit2/src/transports/winhttp.c +130 -11
  93. data/vendor/libgit2/src/tree.c +329 -98
  94. data/vendor/libgit2/src/tree.h +4 -5
  95. data/vendor/libgit2/src/unix/map.c +5 -0
  96. data/vendor/libgit2/src/win32/map.c +24 -5
  97. data/vendor/libgit2/src/xdiff/xprepare.c +2 -1
  98. metadata +10 -4
  99. data/vendor/libgit2/Makefile.embed +0 -60
@@ -8,6 +8,7 @@
8
8
  #define INCLUDE_delta_apply_h__
9
9
 
10
10
  #include "odb.h"
11
+ #include "pack.h"
11
12
 
12
13
  /**
13
14
  * Apply a git binary delta to recover the original content.
@@ -47,4 +48,15 @@ extern int git__delta_read_header(
47
48
  size_t *base_sz,
48
49
  size_t *res_sz);
49
50
 
51
+ /**
52
+ * Read the header of a git binary delta
53
+ *
54
+ * This variant reads just enough from the packfile stream to read the
55
+ * delta header.
56
+ */
57
+ extern int git__delta_read_header_fromstream(
58
+ size_t *base_sz,
59
+ size_t *res_sz,
60
+ git_packfile_stream *stream);
61
+
50
62
  #endif
@@ -197,7 +197,7 @@ static int commit_name_dup(struct commit_name **out, struct commit_name *in)
197
197
  name->tag = NULL;
198
198
  name->path = NULL;
199
199
 
200
- if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0)
200
+ if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
201
201
  return -1;
202
202
 
203
203
  name->path = git__strdup(in->path);
@@ -582,7 +582,8 @@ static int describe(
582
582
  best = (struct possible_tag *)git_vector_get(&all_matches, 0);
583
583
 
584
584
  if (gave_up_on) {
585
- git_pqueue_insert(&list, gave_up_on);
585
+ if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
586
+ goto cleanup;
586
587
  seen_commits--;
587
588
  }
588
589
  if ((error = finish_depth_computation(
@@ -826,8 +826,7 @@ static int maybe_modified(
826
826
  */
827
827
  } else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
828
828
  bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
829
- git_index *index;
830
- git_iterator_index(&index, info->new_iter);
829
+ git_index *index = git_iterator_index(info->new_iter);
831
830
 
832
831
  status = GIT_DELTA_UNMODIFIED;
833
832
 
@@ -980,15 +979,14 @@ static int iterator_advance_into(
980
979
  return error;
981
980
  }
982
981
 
983
- static int iterator_advance_over_with_status(
982
+ static int iterator_advance_over(
984
983
  const git_index_entry **entry,
985
984
  git_iterator_status_t *status,
986
985
  git_iterator *iterator)
987
986
  {
988
- int error;
987
+ int error = git_iterator_advance_over(entry, status, iterator);
989
988
 
990
- if ((error = git_iterator_advance_over_with_status(
991
- entry, status, iterator)) == GIT_ITEROVER) {
989
+ if (error == GIT_ITEROVER) {
992
990
  *entry = NULL;
993
991
  error = 0;
994
992
  }
@@ -1056,7 +1054,7 @@ static int handle_unmatched_new_item(
1056
1054
  return iterator_advance(&info->nitem, info->new_iter);
1057
1055
 
1058
1056
  /* iterate into dir looking for an actual untracked file */
1059
- if ((error = iterator_advance_over_with_status(
1057
+ if ((error = iterator_advance_over(
1060
1058
  &info->nitem, &untracked_state, info->new_iter)) < 0)
1061
1059
  return error;
1062
1060
 
@@ -1085,17 +1083,13 @@ static int handle_unmatched_new_item(
1085
1083
  if (recurse_into_dir) {
1086
1084
  error = iterator_advance_into(&info->nitem, info->new_iter);
1087
1085
 
1088
- /* if real error or no error, proceed with iteration */
1089
- if (error != GIT_ENOTFOUND)
1090
- return error;
1091
- giterr_clear();
1086
+ /* if directory is empty, can't advance into it, so skip it */
1087
+ if (error == GIT_ENOTFOUND) {
1088
+ giterr_clear();
1089
+ error = iterator_advance(&info->nitem, info->new_iter);
1090
+ }
1092
1091
 
1093
- /* if directory is empty, can't advance into it, so either skip
1094
- * it or ignore it
1095
- */
1096
- if (contains_oitem)
1097
- return iterator_advance(&info->nitem, info->new_iter);
1098
- delta_type = GIT_DELTA_IGNORED;
1092
+ return error;
1099
1093
  }
1100
1094
  }
1101
1095
 
@@ -1231,9 +1225,8 @@ int git_diff__from_iterators(
1231
1225
 
1232
1226
  /* make iterators have matching icase behavior */
1233
1227
  if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
1234
- if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
1235
- (error = git_iterator_set_ignore_case(new_iter, true)) < 0)
1236
- goto cleanup;
1228
+ git_iterator_set_ignore_case(old_iter, true);
1229
+ git_iterator_set_ignore_case(new_iter, true);
1237
1230
  }
1238
1231
 
1239
1232
  /* finish initialization */
@@ -261,7 +261,7 @@ static int normalize_find_opts(
261
261
  if (!given ||
262
262
  (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
263
263
  {
264
- if (diff->repo) {
264
+ if (cfg) {
265
265
  char *rule =
266
266
  git_config__get_string_force(cfg, "diff.renames", "true");
267
267
  int boolval;
@@ -318,8 +318,10 @@ static int normalize_find_opts(
318
318
  #undef USE_DEFAULT
319
319
 
320
320
  if (!opts->rename_limit) {
321
- opts->rename_limit = git_config__get_int_force(
322
- cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
321
+ if (cfg) {
322
+ opts->rename_limit = git_config__get_int_force(
323
+ cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
324
+ }
323
325
 
324
326
  if (opts->rename_limit <= 0)
325
327
  opts->rename_limit = DEFAULT_RENAME_LIMIT;
@@ -70,6 +70,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
70
70
  git_file source;
71
71
  char buffer[FILEIO_BUFSIZE];
72
72
  ssize_t read_bytes;
73
+ int error;
73
74
 
74
75
  source = p_open(file->path_original, O_RDONLY);
75
76
  if (source < 0) {
@@ -80,7 +81,8 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
80
81
  }
81
82
 
82
83
  while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
83
- p_write(file->fd, buffer, read_bytes);
84
+ if ((error = p_write(file->fd, buffer, read_bytes)) < 0)
85
+ break;
84
86
  if (file->compute_digest)
85
87
  git_hash_update(&file->digest, buffer, read_bytes);
86
88
  }
@@ -90,6 +92,9 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
90
92
  if (read_bytes < 0) {
91
93
  giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
92
94
  return -1;
95
+ } else if (error < 0) {
96
+ giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock);
97
+ return -1;
93
98
  }
94
99
  }
95
100
 
@@ -267,6 +272,11 @@ cleanup:
267
272
  }
268
273
 
269
274
  int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
275
+ {
276
+ return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE);
277
+ }
278
+
279
+ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size)
270
280
  {
271
281
  int compression, error = -1;
272
282
  size_t path_len, alloc_len;
@@ -281,7 +291,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
281
291
  if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
282
292
  file->do_not_buffer = true;
283
293
 
284
- file->buf_size = WRITE_BUFFER_SIZE;
294
+ file->buf_size = size;
285
295
  file->buf_pos = 0;
286
296
  file->fd = -1;
287
297
  file->last_error = BUFERR_OK;
@@ -79,6 +79,7 @@ int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
79
79
  int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
80
80
 
81
81
  int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
82
+ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size);
82
83
  int git_filebuf_commit(git_filebuf *lock);
83
84
  int git_filebuf_commit_at(git_filebuf *lock, const char *path);
84
85
  void git_filebuf_cleanup(git_filebuf *lock);
@@ -93,11 +93,24 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
93
93
  * It will be restored if/when we recurse below.
94
94
  */
95
95
  if (c == '*') {
96
- flags &= ~FNM_PATHNAME;
97
- while (c == '*')
98
- c = *++pattern;
99
- if (c == '/')
100
- c = *++pattern;
96
+ c = *++pattern;
97
+ /* star-star-slash is at the end, match by default */
98
+ if (c == EOS)
99
+ return 0;
100
+ /* Double-star must be at end or between slashes */
101
+ if (c != '/')
102
+ return (FNM_NOMATCH);
103
+
104
+ c = *++pattern;
105
+ do {
106
+ int e = p_fnmatchx(pattern, string, recurs_flags, recurs);
107
+ if (e != FNM_NOMATCH)
108
+ return e;
109
+ string = strchr(string, '/');
110
+ } while (string++);
111
+
112
+ /* If we get here, we didn't find a match */
113
+ return FNM_NOMATCH;
101
114
  }
102
115
 
103
116
  if (*string == '.' && (flags & FNM_PERIOD) &&
@@ -9,6 +9,7 @@
9
9
  #include "hash.h"
10
10
  #include "sysdir.h"
11
11
  #include "filter.h"
12
+ #include "merge_driver.h"
12
13
  #include "openssl_stream.h"
13
14
  #include "thread-utils.h"
14
15
  #include "git2/global.h"
@@ -27,6 +28,7 @@ static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
27
28
  static git_atomic git__n_shutdown_callbacks;
28
29
  static git_atomic git__n_inits;
29
30
  char *git__user_agent;
31
+ char *git__ssl_ciphers;
30
32
 
31
33
  void git__on_shutdown(git_global_shutdown_fn callback)
32
34
  {
@@ -58,6 +60,7 @@ static int init_common(void)
58
60
  if ((ret = git_hash_global_init()) == 0 &&
59
61
  (ret = git_sysdir_global_init()) == 0 &&
60
62
  (ret = git_filter_global_init()) == 0 &&
63
+ (ret = git_merge_driver_global_init()) == 0 &&
61
64
  (ret = git_transport_ssh_global_init()) == 0)
62
65
  ret = git_openssl_stream_global_init();
63
66
 
@@ -83,6 +86,7 @@ static void shutdown_common(void)
83
86
  }
84
87
 
85
88
  git__free(git__user_agent);
89
+ git__free(git__ssl_ciphers);
86
90
 
87
91
  #if defined(GIT_MSVC_CRTDBG)
88
92
  git_win32__crtdbg_stacktrace_cleanup();
@@ -222,6 +226,20 @@ void git__free_tls_data(void)
222
226
  TlsSetValue(_tls_index, NULL);
223
227
  }
224
228
 
229
+ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
230
+ {
231
+ /* This is how Windows lets us know our thread is being shut down */
232
+ if (fdwReason == DLL_THREAD_DETACH) {
233
+ git__free_tls_data();
234
+ }
235
+
236
+ /*
237
+ * Windows pays attention to this during library loading. We don't do anything
238
+ * so we trivially succeed.
239
+ */
240
+ return TRUE;
241
+ }
242
+
225
243
  #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
226
244
 
227
245
  static pthread_key_t _tls_key;
@@ -36,5 +36,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
36
36
  extern void git__free_tls_data(void);
37
37
 
38
38
  extern const char *git_libgit2__user_agent(void);
39
+ extern const char *git_libgit2__ssl_ciphers(void);
39
40
 
40
41
  #endif
@@ -263,10 +263,18 @@ int git_ignore__for_path(
263
263
  goto cleanup;
264
264
 
265
265
  /* given a unrooted path in a non-bare repo, resolve it */
266
- if (workdir && git_path_root(path) < 0)
267
- error = git_path_find_dir(&ignores->dir, path, workdir);
268
- else
266
+ if (workdir && git_path_root(path) < 0) {
267
+ git_buf local = GIT_BUF_INIT;
268
+
269
+ if ((error = git_path_dirname_r(&local, path)) < 0 ||
270
+ (error = git_path_resolve_relative(&local, 0)) < 0 ||
271
+ (error = git_path_to_dir(&local)) < 0 ||
272
+ (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0)
273
+ {;} /* Nothing, we just want to stop on the first error */
274
+ git_buf_free(&local);
275
+ } else {
269
276
  error = git_buf_joinpath(&ignores->dir, path, "");
277
+ }
270
278
  if (error < 0)
271
279
  goto cleanup;
272
280
 
@@ -963,14 +963,20 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
963
963
  *reuc_out = reuc = reuc_entry_alloc(path);
964
964
  GITERR_CHECK_ALLOC(reuc);
965
965
 
966
- if ((reuc->mode[0] = ancestor_mode) > 0)
966
+ if ((reuc->mode[0] = ancestor_mode) > 0) {
967
+ assert(ancestor_oid);
967
968
  git_oid_cpy(&reuc->oid[0], ancestor_oid);
969
+ }
968
970
 
969
- if ((reuc->mode[1] = our_mode) > 0)
971
+ if ((reuc->mode[1] = our_mode) > 0) {
972
+ assert(our_oid);
970
973
  git_oid_cpy(&reuc->oid[1], our_oid);
974
+ }
971
975
 
972
- if ((reuc->mode[2] = their_mode) > 0)
976
+ if ((reuc->mode[2] = their_mode) > 0) {
977
+ assert(their_oid);
973
978
  git_oid_cpy(&reuc->oid[2], their_oid);
979
+ }
974
980
 
975
981
  return 0;
976
982
  }
@@ -2830,7 +2836,7 @@ static int read_tree_cb(
2830
2836
  return -1;
2831
2837
 
2832
2838
  entry->mode = tentry->attr;
2833
- entry->id = tentry->oid;
2839
+ git_oid_cpy(&entry->id, git_tree_entry_id(tentry));
2834
2840
 
2835
2841
  /* look for corresponding old entry and copy data to new entry */
2836
2842
  if (data->old_entries != NULL &&
@@ -3002,7 +3008,7 @@ int git_index_read_index(
3002
3008
 
3003
3009
  if (error < 0) {
3004
3010
  giterr_set(GITERR_INDEX, "failed to insert entry");
3005
- return error;
3011
+ goto done;
3006
3012
  }
3007
3013
 
3008
3014
  if (diff <= 0) {
@@ -449,7 +449,7 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
449
449
  static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
450
450
  {
451
451
  git_file fd = idx->pack->mwf.fd;
452
- size_t page_size;
452
+ size_t mmap_alignment;
453
453
  size_t page_offset;
454
454
  git_off_t page_start;
455
455
  unsigned char *map_data;
@@ -458,11 +458,11 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t
458
458
 
459
459
  assert(data && size);
460
460
 
461
- if ((error = git__page_size(&page_size)) < 0)
461
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
462
462
  return error;
463
463
 
464
- /* the offset needs to be at the beginning of the a page boundary */
465
- page_offset = offset % page_size;
464
+ /* the offset needs to be at the mmap boundary for the platform */
465
+ page_offset = offset % mmap_alignment;
466
466
  page_start = offset - page_offset;
467
467
 
468
468
  if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
@@ -777,7 +777,6 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
777
777
 
778
778
  curpos = delta->delta_off;
779
779
  error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
780
- git_mwindow_close(&w);
781
780
  if (error < 0)
782
781
  return error;
783
782
 
@@ -914,12 +913,17 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
914
913
  git_filebuf index_file = {0};
915
914
  void *packfile_trailer;
916
915
 
916
+ if (!idx->parsed_header) {
917
+ giterr_set(GITERR_INDEXER, "incomplete pack header");
918
+ return -1;
919
+ }
920
+
917
921
  if (git_hash_ctx_init(&ctx) < 0)
918
922
  return -1;
919
923
 
920
924
  /* Test for this before resolve_deltas(), as it plays with idx->off */
921
- if (idx->off < idx->pack->mwf.size - 20) {
922
- giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack");
925
+ if (idx->off + 20 < idx->pack->mwf.size) {
926
+ giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
923
927
  return -1;
924
928
  }
925
929
 
@@ -8,288 +8,372 @@
8
8
  #include "iterator.h"
9
9
  #include "tree.h"
10
10
  #include "index.h"
11
- #include "ignore.h"
12
- #include "buffer.h"
13
- #include "submodule.h"
14
- #include <ctype.h>
15
-
16
- #define ITERATOR_SET_CB(P,NAME_LC) do { \
17
- (P)->cb.current = NAME_LC ## _iterator__current; \
18
- (P)->cb.advance = NAME_LC ## _iterator__advance; \
19
- (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
20
- (P)->cb.seek = NAME_LC ## _iterator__seek; \
21
- (P)->cb.reset = NAME_LC ## _iterator__reset; \
22
- (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
23
- (P)->cb.free = NAME_LC ## _iterator__free; \
24
- } while (0)
25
-
26
- #define ITERATOR_CASE_FLAGS \
27
- (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
28
-
29
- #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
30
- (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
31
- (P)->base.cb = &(P)->cb; \
32
- ITERATOR_SET_CB(P,NAME_LC); \
33
- (P)->base.repo = (REPO); \
34
- (P)->base.start = options && options->start ? \
35
- git__strdup(options->start) : NULL; \
36
- (P)->base.end = options && options->end ? \
37
- git__strdup(options->end) : NULL; \
38
- if ((options && options->start && !(P)->base.start) || \
39
- (options && options->end && !(P)->base.end)) { \
40
- git__free(P); return -1; } \
41
- (P)->base.strcomp = git__strcmp; \
42
- (P)->base.strncomp = git__strncmp; \
43
- (P)->base.prefixcomp = git__prefixcmp; \
44
- (P)->base.flags = options ? options->flags & ~ITERATOR_CASE_FLAGS : 0; \
45
- if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
46
- (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
47
- if (options && options->pathlist.count && \
48
- iterator_pathlist__init(&P->base, &options->pathlist) < 0) { \
49
- git__free(P); return -1; } \
50
- } while (0)
51
11
 
52
- #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
53
- #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
54
- #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
55
- #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
56
- #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
57
- #define iterator__include_conflicts(I) iterator__flag(I, INCLUDE_CONFLICTS)
12
+ #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
13
+ #define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
14
+ #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
58
15
 
59
- #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
16
+ #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
17
+ #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
18
+ #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
19
+ #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
20
+ #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
21
+ #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
60
22
  #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
23
+ #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
24
+ #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
61
25
 
62
- #define iterator__end(I) ((git_iterator *)(I))->end
63
- #define iterator__past_end(I,PATH) \
64
- (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
65
26
 
27
+ static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
28
+ {
29
+ if (ignore_case)
30
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
31
+ else
32
+ iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
33
+
34
+ iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp;
35
+ iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp;
36
+ iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
37
+ iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
66
38
 
67
- typedef enum {
68
- ITERATOR_PATHLIST_NONE = 0,
69
- ITERATOR_PATHLIST_MATCH = 1,
70
- ITERATOR_PATHLIST_MATCH_DIRECTORY = 2,
71
- ITERATOR_PATHLIST_MATCH_CHILD = 3,
72
- } iterator_pathlist__match_t;
39
+ git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
40
+ }
41
+
42
+ static int iterator_range_init(
43
+ git_iterator *iter, const char *start, const char *end)
44
+ {
45
+ if (start && *start) {
46
+ iter->start = git__strdup(start);
47
+ GITERR_CHECK_ALLOC(iter->start);
48
+
49
+ iter->start_len = strlen(iter->start);
50
+ }
51
+
52
+ if (end && *end) {
53
+ iter->end = git__strdup(end);
54
+ GITERR_CHECK_ALLOC(iter->end);
55
+
56
+ iter->end_len = strlen(iter->end);
57
+ }
58
+
59
+ iter->started = (iter->start == NULL);
60
+ iter->ended = false;
61
+
62
+ return 0;
63
+ }
64
+
65
+ static void iterator_range_free(git_iterator *iter)
66
+ {
67
+ if (iter->start) {
68
+ git__free(iter->start);
69
+ iter->start = NULL;
70
+ iter->start_len = 0;
71
+ }
72
+
73
+ if (iter->end) {
74
+ git__free(iter->end);
75
+ iter->end = NULL;
76
+ iter->end_len = 0;
77
+ }
78
+ }
79
+
80
+ static int iterator_reset_range(
81
+ git_iterator *iter, const char *start, const char *end)
82
+ {
83
+ iterator_range_free(iter);
84
+ return iterator_range_init(iter, start, end);
85
+ }
73
86
 
74
- static int iterator_pathlist__init(git_iterator *iter, git_strarray *pathspec)
87
+ static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist)
75
88
  {
76
89
  size_t i;
77
90
 
78
- if (git_vector_init(&iter->pathlist, pathspec->count,
79
- (git_vector_cmp)iter->strcomp) < 0)
91
+ if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0)
80
92
  return -1;
81
93
 
82
- for (i = 0; i < pathspec->count; i++) {
83
- if (!pathspec->strings[i])
94
+ for (i = 0; i < pathlist->count; i++) {
95
+ if (!pathlist->strings[i])
84
96
  continue;
85
97
 
86
- if (git_vector_insert(&iter->pathlist, pathspec->strings[i]) < 0)
98
+ if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0)
87
99
  return -1;
88
100
  }
89
101
 
90
- git_vector_sort(&iter->pathlist);
91
-
92
102
  return 0;
93
103
  }
94
104
 
95
- static iterator_pathlist__match_t iterator_pathlist__match(
96
- git_iterator *iter, const char *path, size_t path_len)
105
+ static int iterator_init_common(
106
+ git_iterator *iter,
107
+ git_repository *repo,
108
+ git_index *index,
109
+ git_iterator_options *given_opts)
97
110
  {
98
- const char *p;
99
- size_t idx;
111
+ static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT;
112
+ git_iterator_options *options = given_opts ? given_opts : &default_opts;
113
+ bool ignore_case;
114
+ int precompose;
100
115
  int error;
101
116
 
102
- error = git_vector_bsearch2(&idx, &iter->pathlist,
103
- (git_vector_cmp)iter->strcomp, path);
117
+ iter->repo = repo;
118
+ iter->index = index;
119
+ iter->flags = options->flags;
104
120
 
105
- if (error == 0)
106
- return ITERATOR_PATHLIST_MATCH;
121
+ if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
122
+ ignore_case = true;
123
+ } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) {
124
+ ignore_case = false;
125
+ } else if (repo) {
126
+ git_index *index;
107
127
 
108
- /* at this point, the path we're examining may be a directory (though we
109
- * don't know that yet, since we're avoiding a stat unless it's necessary)
110
- * so see if the pathlist contains a file beneath this directory.
111
- */
112
- while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
113
- if (iter->prefixcomp(p, path) != 0)
114
- break;
128
+ if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
129
+ return error;
115
130
 
116
- /* an exact match would have been matched by the bsearch above */
117
- assert(p[path_len]);
131
+ ignore_case = !!index->ignore_case;
118
132
 
119
- /* is this a literal directory entry (eg `foo/`) or a file beneath */
120
- if (p[path_len] == '/') {
121
- return (p[path_len+1] == '\0') ?
122
- ITERATOR_PATHLIST_MATCH_DIRECTORY :
123
- ITERATOR_PATHLIST_MATCH_CHILD;
124
- }
133
+ if (ignore_case == 1)
134
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
135
+ else
136
+ iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
137
+ } else {
138
+ ignore_case = false;
139
+ }
125
140
 
126
- if (p[path_len] > '/')
127
- break;
141
+ /* try to look up precompose and set flag if appropriate */
142
+ if (repo &&
143
+ (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 &&
144
+ (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) {
128
145
 
129
- idx++;
146
+ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
147
+ giterr_clear();
148
+ else if (precompose)
149
+ iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
130
150
  }
131
151
 
132
- return ITERATOR_PATHLIST_NONE;
152
+ if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND))
153
+ iter->flags |= GIT_ITERATOR_INCLUDE_TREES;
154
+
155
+ if ((error = iterator_range_init(iter, options->start, options->end)) < 0 ||
156
+ (error = iterator_pathlist_init(iter, &options->pathlist)) < 0)
157
+ return error;
158
+
159
+ iterator_set_ignore_case(iter, ignore_case);
160
+ return 0;
133
161
  }
134
162
 
135
- static void iterator_pathlist_walk__reset(git_iterator *iter)
163
+ static void iterator_clear(git_iterator *iter)
136
164
  {
165
+ iter->started = false;
166
+ iter->ended = false;
167
+ iter->stat_calls = 0;
137
168
  iter->pathlist_walk_idx = 0;
169
+ iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
170
+ }
171
+
172
+ GIT_INLINE(bool) iterator_has_started(
173
+ git_iterator *iter, const char *path, bool is_submodule)
174
+ {
175
+ size_t path_len;
176
+
177
+ if (iter->start == NULL || iter->started == true)
178
+ return true;
179
+
180
+ /* the starting path is generally a prefix - we have started once we
181
+ * are prefixed by this path
182
+ */
183
+ iter->started = (iter->prefixcomp(path, iter->start) >= 0);
184
+
185
+ if (iter->started)
186
+ return true;
187
+
188
+ path_len = strlen(path);
189
+
190
+ /* if, however, we are a submodule, then we support `start` being
191
+ * suffixed with a `/` for crazy legacy reasons. match `submod`
192
+ * with a start path of `submod/`.
193
+ */
194
+ if (is_submodule && iter->start_len && path_len == iter->start_len - 1 &&
195
+ iter->start[iter->start_len-1] == '/')
196
+ return true;
197
+
198
+ /* if, however, our current path is a directory, and our starting path
199
+ * is _beneath_ that directory, then recurse into the directory (even
200
+ * though we have not yet "started")
201
+ */
202
+ if (path_len > 0 && path[path_len-1] == '/' &&
203
+ iter->strncomp(path, iter->start, path_len) == 0)
204
+ return true;
205
+
206
+ return false;
207
+ }
208
+
209
+ GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path)
210
+ {
211
+ if (iter->end == NULL)
212
+ return false;
213
+ else if (iter->ended)
214
+ return true;
215
+
216
+ iter->ended = (iter->prefixcomp(path, iter->end) > 0);
217
+ return iter->ended;
138
218
  }
139
219
 
140
- /* walker for the index iterator that allows it to walk the sorted pathlist
141
- * entries alongside the sorted index entries. the `iter->pathlist_walk_idx`
142
- * stores the starting position for subsequent calls, the position is advanced
143
- * along with the index iterator, with a special case for handling directories
144
- * in the pathlist that are specified without trailing '/'. (eg, `foo`).
145
- * we do not advance over these entries until we're certain that the index
146
- * iterator will not ask us for a file beneath that directory (eg, `foo/bar`).
220
+ /* walker for the index and tree iterator that allows it to walk the sorted
221
+ * pathlist entries alongside sorted iterator entries.
147
222
  */
148
- static bool iterator_pathlist_walk__contains(git_iterator *iter, const char *path)
223
+ static bool iterator_pathlist_next_is(git_iterator *iter, const char *path)
149
224
  {
150
- size_t i;
151
225
  char *p;
152
- size_t p_len;
226
+ size_t path_len, p_len, cmp_len, i;
153
227
  int cmp;
154
228
 
229
+ if (iter->pathlist.length == 0)
230
+ return true;
231
+
232
+ git_vector_sort(&iter->pathlist);
233
+
234
+ path_len = strlen(path);
235
+
236
+ /* for comparison, drop the trailing slash on the current '/' */
237
+ if (path_len && path[path_len-1] == '/')
238
+ path_len--;
239
+
155
240
  for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
156
241
  p = iter->pathlist.contents[i];
157
242
  p_len = strlen(p);
158
243
 
244
+ if (p_len && p[p_len-1] == '/')
245
+ p_len--;
246
+
247
+ cmp_len = min(path_len, p_len);
248
+
159
249
  /* see if the pathlist entry is a prefix of this path */
160
- cmp = iter->strncomp(p, path, p_len);
250
+ cmp = iter->strncomp(p, path, cmp_len);
251
+
252
+ /* prefix match - see if there's an exact match, or if we were
253
+ * given a path that matches the directory
254
+ */
255
+ if (cmp == 0) {
256
+ /* if this pathlist entry is not suffixed with a '/' then
257
+ * it matches a path that is a file or a directory.
258
+ * (eg, pathlist = "foo" and path is "foo" or "foo/" or
259
+ * "foo/something")
260
+ */
261
+ if (p[cmp_len] == '\0' &&
262
+ (path[cmp_len] == '\0' || path[cmp_len] == '/'))
263
+ return true;
264
+
265
+ /* if this pathlist entry _is_ suffixed with a '/' then
266
+ * it matches only paths that are directories.
267
+ * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
268
+ */
269
+ if (p[cmp_len] == '/' && path[cmp_len] == '/')
270
+ return true;
271
+ }
161
272
 
162
273
  /* this pathlist entry sorts before the given path, try the next */
163
- if (!p_len || cmp < 0)
274
+ else if (cmp < 0) {
164
275
  iter->pathlist_walk_idx++;
276
+ continue;
277
+ }
165
278
 
166
279
  /* this pathlist sorts after the given path, no match. */
167
- else if (cmp > 0)
168
- return false;
169
-
170
- /* match! an exact match (`foo` vs `foo`), the path is a child of an
171
- * explicit directory in the pathlist (`foo/` vs `foo/bar`) or the path
172
- * is a child of an entry in the pathlist (`foo` vs `foo/bar`)
173
- */
174
- else if (path[p_len] == '\0' || p[p_len - 1] == '/' || path[p_len] == '/')
175
- return true;
176
-
177
- /* only advance the start index for future callers if we know that we
178
- * will not see a child of this path. eg, a pathlist entry `foo` is
179
- * a prefix for `foo.txt` and `foo/bar`. don't advance the start
180
- * pathlist index when we see `foo.txt` or we would miss a subsequent
181
- * inspection of `foo/bar`. only advance when there are no more
182
- * potential children.
183
- */
184
- else if (path[p_len] > '/')
185
- iter->pathlist_walk_idx++;
280
+ else if (cmp > 0) {
281
+ break;
282
+ }
186
283
  }
187
284
 
188
285
  return false;
189
286
  }
190
287
 
191
- static void iterator_pathlist__update_ignore_case(git_iterator *iter)
192
- {
193
- git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
194
- git_vector_sort(&iter->pathlist);
195
-
196
- iter->pathlist_walk_idx = 0;
197
- }
198
-
288
+ typedef enum {
289
+ ITERATOR_PATHLIST_NONE = 0,
290
+ ITERATOR_PATHLIST_IS_FILE = 1,
291
+ ITERATOR_PATHLIST_IS_DIR = 2,
292
+ ITERATOR_PATHLIST_IS_PARENT = 3,
293
+ ITERATOR_PATHLIST_FULL = 4,
294
+ } iterator_pathlist_search_t;
199
295
 
200
- static int iterator__reset_range(
201
- git_iterator *iter, const char *start, const char *end)
296
+ static iterator_pathlist_search_t iterator_pathlist_search(
297
+ git_iterator *iter, const char *path, size_t path_len)
202
298
  {
203
- if (start) {
204
- if (iter->start)
205
- git__free(iter->start);
206
- iter->start = git__strdup(start);
207
- GITERR_CHECK_ALLOC(iter->start);
208
- }
209
-
210
- if (end) {
211
- if (iter->end)
212
- git__free(iter->end);
213
- iter->end = git__strdup(end);
214
- GITERR_CHECK_ALLOC(iter->end);
215
- }
216
-
217
- iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
299
+ const char *p;
300
+ size_t idx;
301
+ int error;
218
302
 
219
- return 0;
220
- }
303
+ if (iter->pathlist.length == 0)
304
+ return ITERATOR_PATHLIST_FULL;
221
305
 
222
- static int iterator__update_ignore_case(
223
- git_iterator *iter,
224
- git_iterator_flag_t flags)
225
- {
226
- bool ignore_case;
227
- int error;
306
+ git_vector_sort(&iter->pathlist);
228
307
 
229
- if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
230
- ignore_case = true;
231
- else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
232
- ignore_case = false;
233
- else {
234
- git_index *index;
308
+ error = git_vector_bsearch2(&idx, &iter->pathlist,
309
+ (git_vector_cmp)iter->strcomp, path);
235
310
 
236
- if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
237
- return error;
311
+ /* the given path was found in the pathlist. since the pathlist only
312
+ * matches directories when they're suffixed with a '/', analyze the
313
+ * path string to determine whether it's a directory or not.
314
+ */
315
+ if (error == 0) {
316
+ if (path_len && path[path_len-1] == '/')
317
+ return ITERATOR_PATHLIST_IS_DIR;
238
318
 
239
- ignore_case = (index->ignore_case == 1);
319
+ return ITERATOR_PATHLIST_IS_FILE;
240
320
  }
241
321
 
242
- if (ignore_case) {
243
- iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
322
+ /* at this point, the path we're examining may be a directory (though we
323
+ * don't know that yet, since we're avoiding a stat unless it's necessary)
324
+ * so walk the pathlist looking for the given path with a '/' after it,
325
+ */
326
+ while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
327
+ if (iter->prefixcomp(p, path) != 0)
328
+ break;
244
329
 
245
- iter->strcomp = git__strcasecmp;
246
- iter->strncomp = git__strncasecmp;
247
- iter->prefixcomp = git__prefixcmp_icase;
248
- } else {
249
- iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
330
+ /* an exact match would have been matched by the bsearch above */
331
+ assert(p[path_len]);
250
332
 
251
- iter->strcomp = git__strcmp;
252
- iter->strncomp = git__strncmp;
253
- iter->prefixcomp = git__prefixcmp;
254
- }
333
+ /* is this a literal directory entry (eg `foo/`) or a file beneath */
334
+ if (p[path_len] == '/') {
335
+ return (p[path_len+1] == '\0') ?
336
+ ITERATOR_PATHLIST_IS_DIR :
337
+ ITERATOR_PATHLIST_IS_PARENT;
338
+ }
255
339
 
256
- iterator_pathlist__update_ignore_case(iter);
340
+ if (p[path_len] > '/')
341
+ break;
257
342
 
258
- return 0;
259
- }
343
+ idx++;
344
+ }
260
345
 
261
- GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
262
- {
263
- if (entry) *entry = NULL;
346
+ return ITERATOR_PATHLIST_NONE;
264
347
  }
265
348
 
349
+ /* Empty iterator */
266
350
 
267
- static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
351
+ static int empty_iterator_noop(const git_index_entry **e, git_iterator *i)
268
352
  {
269
353
  GIT_UNUSED(i);
270
- iterator__clear_entry(e);
271
- return GIT_ITEROVER;
272
- }
273
354
 
274
- static int empty_iterator__seek(git_iterator *i, const char *p)
275
- {
276
- GIT_UNUSED(i); GIT_UNUSED(p);
277
- return -1;
355
+ if (e)
356
+ *e = NULL;
357
+
358
+ return GIT_ITEROVER;
278
359
  }
279
360
 
280
- static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
361
+ static int empty_iterator_advance_over(
362
+ const git_index_entry **e,
363
+ git_iterator_status_t *s,
364
+ git_iterator *i)
281
365
  {
282
- GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
283
- return 0;
366
+ *s = GIT_ITERATOR_STATUS_EMPTY;
367
+ return empty_iterator_noop(e, i);
284
368
  }
285
369
 
286
- static int empty_iterator__at_end(git_iterator *i)
370
+ static int empty_iterator_reset(git_iterator *i)
287
371
  {
288
372
  GIT_UNUSED(i);
289
- return 1;
373
+ return 0;
290
374
  }
291
375
 
292
- static void empty_iterator__free(git_iterator *i)
376
+ static void empty_iterator_free(git_iterator *i)
293
377
  {
294
378
  GIT_UNUSED(i);
295
379
  }
@@ -300,1516 +384,1512 @@ typedef struct {
300
384
  } empty_iterator;
301
385
 
302
386
  int git_iterator_for_nothing(
303
- git_iterator **iter,
387
+ git_iterator **out,
304
388
  git_iterator_options *options)
305
389
  {
306
- empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
307
- GITERR_CHECK_ALLOC(i);
390
+ empty_iterator *iter;
308
391
 
309
- #define empty_iterator__current empty_iterator__noop
310
- #define empty_iterator__advance empty_iterator__noop
311
- #define empty_iterator__advance_into empty_iterator__noop
392
+ static git_iterator_callbacks callbacks = {
393
+ empty_iterator_noop,
394
+ empty_iterator_noop,
395
+ empty_iterator_noop,
396
+ empty_iterator_advance_over,
397
+ empty_iterator_reset,
398
+ empty_iterator_free
399
+ };
312
400
 
313
- ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
401
+ *out = NULL;
314
402
 
315
- if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0)
316
- i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
403
+ iter = git__calloc(1, sizeof(empty_iterator));
404
+ GITERR_CHECK_ALLOC(iter);
317
405
 
318
- *iter = (git_iterator *)i;
406
+ iter->base.type = GIT_ITERATOR_TYPE_EMPTY;
407
+ iter->base.cb = &callbacks;
408
+ iter->base.flags = options->flags;
409
+
410
+ *out = &iter->base;
319
411
  return 0;
320
412
  }
321
413
 
414
+ /* Tree iterator */
415
+
416
+ typedef struct {
417
+ git_tree_entry *tree_entry;
418
+ const char *parent_path;
419
+ } tree_iterator_entry;
322
420
 
323
- typedef struct tree_iterator_entry tree_iterator_entry;
324
- struct tree_iterator_entry {
325
- tree_iterator_entry *parent;
326
- const git_tree_entry *te;
421
+ typedef struct {
327
422
  git_tree *tree;
328
- };
329
423
 
330
- typedef struct tree_iterator_frame tree_iterator_frame;
331
- struct tree_iterator_frame {
332
- tree_iterator_frame *up, *down;
424
+ /* path to this particular frame (folder) */
425
+ git_buf path;
333
426
 
334
- size_t n_entries; /* items in this frame */
335
- size_t current; /* start of currently active range in frame */
336
- size_t next; /* start of next range in frame */
427
+ /* a sorted list of the entries for this frame (folder), these are
428
+ * actually pointers to the iterator's entry pool.
429
+ */
430
+ git_vector entries;
431
+ tree_iterator_entry *current;
337
432
 
338
- const char *start;
339
- size_t startlen;
433
+ size_t next_idx;
340
434
 
341
- tree_iterator_entry *entries[GIT_FLEX_ARRAY];
342
- };
435
+ /* on case insensitive iterations, we also have an array of other
436
+ * paths that were case insensitively equal to this one, and their
437
+ * tree objects. we have coalesced the tree entries into this frame.
438
+ * a child `tree_iterator_entry` will contain a pointer to its actual
439
+ * parent path.
440
+ */
441
+ git_vector similar_trees;
442
+ git_array_t(git_buf) similar_paths;
443
+ } tree_iterator_frame;
343
444
 
344
445
  typedef struct {
345
446
  git_iterator base;
346
- git_iterator_callbacks cb;
347
- tree_iterator_frame *head, *root;
348
- git_pool pool;
447
+ git_tree *root;
448
+ git_array_t(tree_iterator_frame) frames;
449
+
349
450
  git_index_entry entry;
350
- git_buf path;
351
- int path_ambiguities;
352
- bool path_has_filename;
353
- bool entry_is_current;
451
+ git_buf entry_path;
452
+
453
+ /* a pool of entries to reduce the number of allocations */
454
+ git_pool entry_pool;
354
455
  } tree_iterator;
355
456
 
356
- static char *tree_iterator__current_filename(
357
- tree_iterator *ti, const git_tree_entry *te)
457
+ GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame(
458
+ tree_iterator *iter)
358
459
  {
359
- if (!ti->path_has_filename) {
360
- if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
361
- return NULL;
362
-
363
- if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
364
- return NULL;
365
-
366
- ti->path_has_filename = true;
367
- }
368
-
369
- return ti->path.ptr;
460
+ return iter->frames.size > 1 ?
461
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
370
462
  }
371
463
 
372
- static void tree_iterator__rewrite_filename(tree_iterator *ti)
464
+ GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame(
465
+ tree_iterator *iter)
373
466
  {
374
- tree_iterator_entry *scan = ti->head->entries[ti->head->current];
375
- ssize_t strpos = ti->path.size;
376
- const git_tree_entry *te;
377
-
378
- if (strpos && ti->path.ptr[strpos - 1] == '/')
379
- strpos--;
380
-
381
- for (; scan && (te = scan->te); scan = scan->parent) {
382
- strpos -= te->filename_len;
383
- memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
384
- strpos -= 1; /* separator */
385
- }
467
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
386
468
  }
387
469
 
388
- static int tree_iterator__te_cmp(
389
- const git_tree_entry *a,
390
- const git_tree_entry *b,
391
- int (*compare)(const char *, const char *, size_t))
470
+ GIT_INLINE(int) tree_entry_cmp(
471
+ const git_tree_entry *a, const git_tree_entry *b, bool icase)
392
472
  {
393
473
  return git_path_cmp(
394
474
  a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
395
475
  b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
396
- compare);
476
+ icase ? git__strncasecmp : git__strncmp);
397
477
  }
398
478
 
399
- static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
479
+ GIT_INLINE(int) tree_iterator_entry_cmp(const void *ptr_a, const void *ptr_b)
400
480
  {
401
- const tree_iterator_entry *ae = a, *be = b;
402
- int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
481
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
482
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
403
483
 
404
- if (!cmp) {
405
- /* stabilize sort order among equivalent names */
406
- if (!ae->parent->te || !be->parent->te)
407
- cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
408
- else
409
- cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
410
- }
484
+ return tree_entry_cmp(a->tree_entry, b->tree_entry, false);
485
+ }
411
486
 
412
- return cmp;
487
+ GIT_INLINE(int) tree_iterator_entry_cmp_icase(
488
+ const void *ptr_a, const void *ptr_b)
489
+ {
490
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
491
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
492
+
493
+ return tree_entry_cmp(a->tree_entry, b->tree_entry, true);
413
494
  }
414
495
 
415
- static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
496
+ static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b)
416
497
  {
417
- const tree_iterator_frame *tf = key;
418
- const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
498
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
499
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
419
500
 
420
- return git_path_cmp(
421
- tf->start, tf->startlen, false,
422
- te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
423
- ((git_iterator *)p)->strncomp);
501
+ int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true);
502
+
503
+ /* stabilize the sort order for filenames that are (case insensitively)
504
+ * the same by examining the parent path (case sensitively) before
505
+ * falling back to a case sensitive sort of the filename.
506
+ */
507
+ if (!c && a->parent_path != b->parent_path)
508
+ c = git__strcmp(a->parent_path, b->parent_path);
509
+
510
+ if (!c)
511
+ c = tree_entry_cmp(a->tree_entry, b->tree_entry, false);
512
+
513
+ return c;
424
514
  }
425
515
 
426
- static bool tree_iterator__move_to_next(
427
- tree_iterator *ti, tree_iterator_frame *tf)
516
+ static int tree_iterator_compute_path(
517
+ git_buf *out,
518
+ tree_iterator_entry *entry)
428
519
  {
429
- if (tf->next > tf->current + 1)
430
- ti->path_ambiguities--;
520
+ git_buf_clear(out);
431
521
 
432
- if (!tf->up) { /* at root */
433
- tf->current = tf->next;
434
- return false;
435
- }
522
+ if (entry->parent_path)
523
+ git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename);
524
+ else
525
+ git_buf_puts(out, entry->tree_entry->filename);
436
526
 
437
- for (; tf->current < tf->next; tf->current++) {
438
- git_tree_free(tf->entries[tf->current]->tree);
439
- tf->entries[tf->current]->tree = NULL;
440
- }
527
+ if (git_tree_entry__is_tree(entry->tree_entry))
528
+ git_buf_putc(out, '/');
529
+
530
+ if (git_buf_oom(out))
531
+ return -1;
441
532
 
442
- return (tf->current < tf->n_entries);
533
+ return 0;
443
534
  }
444
535
 
445
- static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
536
+ static int tree_iterator_frame_init(
537
+ tree_iterator *iter,
538
+ git_tree *tree,
539
+ tree_iterator_entry *frame_entry)
446
540
  {
541
+ tree_iterator_frame *new_frame = NULL;
542
+ tree_iterator_entry *new_entry;
543
+ git_tree *dup = NULL;
544
+ git_tree_entry *tree_entry;
545
+ git_vector_cmp cmp;
546
+ size_t i;
447
547
  int error = 0;
448
- const git_tree_entry *te, *last = NULL;
449
548
 
450
- tf->next = tf->current;
549
+ new_frame = git_array_alloc(iter->frames);
550
+ GITERR_CHECK_ALLOC(new_frame);
451
551
 
452
- for (; tf->next < tf->n_entries; tf->next++, last = te) {
453
- te = tf->entries[tf->next]->te;
552
+ memset(new_frame, 0, sizeof(tree_iterator_frame));
454
553
 
455
- if (last && tree_iterator__te_cmp(last, te, ti->base.strncomp))
456
- break;
554
+ if ((error = git_tree_dup(&dup, tree)) < 0)
555
+ goto done;
556
+
557
+ memset(new_frame, 0x0, sizeof(tree_iterator_frame));
558
+ new_frame->tree = dup;
559
+
560
+ if (frame_entry &&
561
+ (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0)
562
+ goto done;
563
+
564
+ cmp = iterator__ignore_case(&iter->base) ?
565
+ tree_iterator_entry_sort_icase : NULL;
566
+
567
+ if ((error = git_vector_init(
568
+ &new_frame->entries, dup->entries.size, cmp)) < 0)
569
+ goto done;
457
570
 
458
- /* try to load trees for items in [current,next) range */
459
- if (!error && git_tree_entry__is_tree(te))
460
- error = git_tree_lookup(
461
- &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
571
+ git_array_foreach(dup->entries, i, tree_entry) {
572
+ new_entry = git_pool_malloc(&iter->entry_pool, 1);
573
+ GITERR_CHECK_ALLOC(new_entry);
574
+
575
+ new_entry->tree_entry = tree_entry;
576
+ new_entry->parent_path = new_frame->path.ptr;
577
+
578
+ if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0)
579
+ goto done;
462
580
  }
463
581
 
464
- if (tf->next > tf->current + 1)
465
- ti->path_ambiguities++;
582
+ git_vector_set_sorted(&new_frame->entries,
583
+ !iterator__ignore_case(&iter->base));
466
584
 
467
- /* if a tree lookup failed, advance over this span and return failure */
585
+ done:
468
586
  if (error < 0) {
469
- tree_iterator__move_to_next(ti, tf);
470
- return error;
587
+ git_tree_free(dup);
588
+ git_array_pop(iter->frames);
471
589
  }
472
590
 
473
- if (last && !tree_iterator__current_filename(ti, last))
474
- return -1; /* must have been allocation failure */
475
-
476
- return 0;
591
+ return error;
477
592
  }
478
593
 
479
- GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
594
+ GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry(
595
+ tree_iterator_frame *frame)
480
596
  {
481
- return (ti->head->current < ti->head->n_entries &&
482
- ti->head->entries[ti->head->current]->tree != NULL);
597
+ return frame->current;
483
598
  }
484
599
 
485
- static int tree_iterator__push_frame(tree_iterator *ti)
600
+ GIT_INLINE(int) tree_iterator_frame_push_neighbors(
601
+ tree_iterator *iter,
602
+ tree_iterator_frame *parent_frame,
603
+ tree_iterator_frame *frame,
604
+ const char *filename)
486
605
  {
606
+ tree_iterator_entry *entry, *new_entry;
607
+ git_tree *tree = NULL;
608
+ git_tree_entry *tree_entry;
609
+ git_buf *path;
610
+ size_t new_size, i;
487
611
  int error = 0;
488
- tree_iterator_frame *head = ti->head, *tf = NULL;
489
- size_t i, n_entries = 0, alloclen;
490
612
 
491
- if (head->current >= head->n_entries || !head->entries[head->current]->tree)
492
- return GIT_ITEROVER;
493
-
494
- for (i = head->current; i < head->next; ++i)
495
- n_entries += git_tree_entrycount(head->entries[i]->tree);
613
+ while (parent_frame->next_idx < parent_frame->entries.length) {
614
+ entry = parent_frame->entries.contents[parent_frame->next_idx];
496
615
 
497
- GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, sizeof(tree_iterator_entry *), n_entries);
498
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, sizeof(tree_iterator_frame));
616
+ if (strcasecmp(filename, entry->tree_entry->filename) != 0)
617
+ break;
499
618
 
500
- tf = git__calloc(1, alloclen);
501
- GITERR_CHECK_ALLOC(tf);
619
+ if ((error = git_tree_lookup(&tree,
620
+ iter->base.repo, entry->tree_entry->oid)) < 0)
621
+ break;
502
622
 
503
- tf->n_entries = n_entries;
623
+ if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
624
+ break;
504
625
 
505
- tf->up = head;
506
- head->down = tf;
507
- ti->head = tf;
626
+ path = git_array_alloc(parent_frame->similar_paths);
627
+ GITERR_CHECK_ALLOC(path);
508
628
 
509
- for (i = head->current, n_entries = 0; i < head->next; ++i) {
510
- git_tree *tree = head->entries[i]->tree;
511
- size_t j, max_j = git_tree_entrycount(tree);
629
+ memset(path, 0, sizeof(git_buf));
512
630
 
513
- for (j = 0; j < max_j; ++j) {
514
- tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
515
- GITERR_CHECK_ALLOC(entry);
631
+ if ((error = tree_iterator_compute_path(path, entry)) < 0)
632
+ break;
516
633
 
517
- entry->parent = head->entries[i];
518
- entry->te = git_tree_entry_byindex(tree, j);
519
- entry->tree = NULL;
634
+ GITERR_CHECK_ALLOC_ADD(&new_size,
635
+ frame->entries.length, tree->entries.size);
636
+ git_vector_size_hint(&frame->entries, new_size);
520
637
 
521
- tf->entries[n_entries++] = entry;
522
- }
523
- }
638
+ git_array_foreach(tree->entries, i, tree_entry) {
639
+ new_entry = git_pool_malloc(&iter->entry_pool, 1);
640
+ GITERR_CHECK_ALLOC(new_entry);
524
641
 
525
- /* if ignore_case, sort entries case insensitively */
526
- if (iterator__ignore_case(ti))
527
- git__tsort_r(
528
- (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
642
+ new_entry->tree_entry = tree_entry;
643
+ new_entry->parent_path = path->ptr;
529
644
 
530
- /* pick tf->current based on "start" (or start at zero) */
531
- if (head->startlen > 0) {
532
- git__bsearch_r((void **)tf->entries, tf->n_entries, head,
533
- tree_iterator__search_cmp, ti, &tf->current);
645
+ if ((error = git_vector_insert(&frame->entries, new_entry)) < 0)
646
+ break;
647
+ }
534
648
 
535
- while (tf->current &&
536
- !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
537
- tf->current--;
649
+ if (error)
650
+ break;
538
651
 
539
- if ((tf->start = strchr(head->start, '/')) != NULL) {
540
- tf->start++;
541
- tf->startlen = strlen(tf->start);
542
- }
652
+ parent_frame->next_idx++;
543
653
  }
544
654
 
545
- ti->path_has_filename = ti->entry_is_current = false;
655
+ return error;
656
+ }
546
657
 
547
- if ((error = tree_iterator__set_next(ti, tf)) < 0)
548
- return error;
658
+ GIT_INLINE(int) tree_iterator_frame_push(
659
+ tree_iterator *iter, tree_iterator_entry *entry)
660
+ {
661
+ tree_iterator_frame *parent_frame, *frame;
662
+ git_tree *tree = NULL;
663
+ int error;
549
664
 
550
- /* autoexpand as needed */
551
- if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
552
- return tree_iterator__push_frame(ti);
665
+ if ((error = git_tree_lookup(&tree,
666
+ iter->base.repo, entry->tree_entry->oid)) < 0 ||
667
+ (error = tree_iterator_frame_init(iter, tree, entry)) < 0)
668
+ goto done;
553
669
 
554
- return 0;
555
- }
670
+ parent_frame = tree_iterator_parent_frame(iter);
671
+ frame = tree_iterator_current_frame(iter);
556
672
 
557
- static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
558
- {
559
- tree_iterator_frame *tf = ti->head;
673
+ /* if we're case insensitive, then we may have another directory that
674
+ * is (case insensitively) equal to this one. coalesce those children
675
+ * into this tree.
676
+ */
677
+ if (iterator__ignore_case(&iter->base))
678
+ error = tree_iterator_frame_push_neighbors(iter,
679
+ parent_frame, frame, entry->tree_entry->filename);
560
680
 
561
- assert(tf);
681
+ done:
682
+ git_tree_free(tree);
683
+ return error;
684
+ }
562
685
 
563
- if (!tf->up)
564
- return false;
686
+ static void tree_iterator_frame_pop(tree_iterator *iter)
687
+ {
688
+ tree_iterator_frame *frame;
689
+ git_buf *buf = NULL;
690
+ git_tree *tree;
691
+ size_t i;
565
692
 
566
- ti->head = tf->up;
567
- ti->head->down = NULL;
693
+ assert(iter->frames.size);
568
694
 
569
- tree_iterator__move_to_next(ti, tf);
695
+ frame = git_array_pop(iter->frames);
570
696
 
571
- if (!final) { /* if final, don't bother to clean up */
572
- // TODO: maybe free the pool so far?
573
- git_buf_rtruncate_at_char(&ti->path, '/');
574
- }
697
+ git_vector_free(&frame->entries);
698
+ git_tree_free(frame->tree);
575
699
 
576
- git__free(tf);
700
+ do {
701
+ buf = git_array_pop(frame->similar_paths);
702
+ git_buf_free(buf);
703
+ } while (buf != NULL);
577
704
 
578
- return true;
579
- }
705
+ git_array_clear(frame->similar_paths);
580
706
 
581
- static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
582
- {
583
- while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
707
+ git_vector_foreach(&frame->similar_trees, i, tree)
708
+ git_tree_free(tree);
584
709
 
585
- if (!final) {
586
- assert(ti->head);
710
+ git_vector_free(&frame->similar_trees);
587
711
 
588
- ti->head->current = to_end ? ti->head->n_entries : 0;
589
- ti->path_ambiguities = 0;
590
- git_buf_clear(&ti->path);
591
- }
712
+ git_buf_free(&frame->path);
592
713
  }
593
714
 
594
- static int tree_iterator__update_entry(tree_iterator *ti)
715
+ static int tree_iterator_current(
716
+ const git_index_entry **out, git_iterator *i)
595
717
  {
596
- tree_iterator_frame *tf;
597
- const git_tree_entry *te;
598
-
599
- if (ti->entry_is_current)
600
- return 0;
601
-
602
- tf = ti->head;
603
- te = tf->entries[tf->current]->te;
718
+ tree_iterator *iter = (tree_iterator *)i;
604
719
 
605
- ti->entry.mode = te->attr;
606
- git_oid_cpy(&ti->entry.id, &te->oid);
720
+ if (!iterator__has_been_accessed(i))
721
+ return iter->base.cb->advance(out, i);
607
722
 
608
- ti->entry.path = tree_iterator__current_filename(ti, te);
609
- GITERR_CHECK_ALLOC(ti->entry.path);
610
-
611
- if (ti->path_ambiguities > 0)
612
- tree_iterator__rewrite_filename(ti);
613
-
614
- if (iterator__past_end(ti, ti->entry.path)) {
615
- tree_iterator__pop_all(ti, true, false);
723
+ if (!iter->frames.size) {
724
+ *out = NULL;
616
725
  return GIT_ITEROVER;
617
726
  }
618
727
 
619
- ti->entry_is_current = true;
620
-
728
+ *out = &iter->entry;
621
729
  return 0;
622
730
  }
623
731
 
624
- static int tree_iterator__current_internal(
625
- const git_index_entry **entry, git_iterator *self)
732
+ static void tree_iterator_set_current(
733
+ tree_iterator *iter,
734
+ tree_iterator_frame *frame,
735
+ tree_iterator_entry *entry)
626
736
  {
627
- int error;
628
- tree_iterator *ti = (tree_iterator *)self;
629
- tree_iterator_frame *tf = ti->head;
630
-
631
- iterator__clear_entry(entry);
632
-
633
- if (tf->current >= tf->n_entries)
634
- return GIT_ITEROVER;
737
+ git_tree_entry *tree_entry = entry->tree_entry;
635
738
 
636
- if ((error = tree_iterator__update_entry(ti)) < 0)
637
- return error;
638
-
639
- if (entry)
640
- *entry = &ti->entry;
739
+ frame->current = entry;
641
740
 
642
- ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
741
+ memset(&iter->entry, 0x0, sizeof(git_index_entry));
643
742
 
644
- return 0;
743
+ iter->entry.mode = tree_entry->attr;
744
+ iter->entry.path = iter->entry_path.ptr;
745
+ git_oid_cpy(&iter->entry.id, tree_entry->oid);
645
746
  }
646
747
 
647
- static int tree_iterator__advance_into_internal(git_iterator *self)
748
+ static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
648
749
  {
750
+ tree_iterator *iter = (tree_iterator *)i;
649
751
  int error = 0;
650
- tree_iterator *ti = (tree_iterator *)self;
651
-
652
- if (tree_iterator__at_tree(ti))
653
- error = tree_iterator__push_frame(ti);
654
752
 
655
- return error;
656
- }
657
-
658
- static int tree_iterator__advance_internal(git_iterator *self)
659
- {
660
- int error;
661
- tree_iterator *ti = (tree_iterator *)self;
662
- tree_iterator_frame *tf = ti->head;
753
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
663
754
 
664
- if (tf->current >= tf->n_entries)
665
- return GIT_ITEROVER;
755
+ /* examine tree entries until we find the next one to return */
756
+ while (true) {
757
+ tree_iterator_entry *prev_entry, *entry;
758
+ tree_iterator_frame *frame;
759
+ bool is_tree;
666
760
 
667
- if (!iterator__has_been_accessed(ti))
668
- return 0;
761
+ if ((frame = tree_iterator_current_frame(iter)) == NULL) {
762
+ error = GIT_ITEROVER;
763
+ break;
764
+ }
669
765
 
670
- if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
671
- tree_iterator__at_tree(ti))
672
- return tree_iterator__advance_into_internal(self);
766
+ /* no more entries in this frame. pop the frame out */
767
+ if (frame->next_idx == frame->entries.length) {
768
+ tree_iterator_frame_pop(iter);
769
+ continue;
770
+ }
673
771
 
674
- if (ti->path_has_filename) {
675
- git_buf_rtruncate_at_char(&ti->path, '/');
676
- ti->path_has_filename = ti->entry_is_current = false;
677
- }
772
+ /* we may have coalesced the contents of case-insensitively same-named
773
+ * directories, so do the sort now.
774
+ */
775
+ if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries))
776
+ git_vector_sort(&frame->entries);
678
777
 
679
- /* scan forward and up, advancing in frame or popping frame when done */
680
- while (!tree_iterator__move_to_next(ti, tf) &&
681
- tree_iterator__pop_frame(ti, false))
682
- tf = ti->head;
778
+ /* we have more entries in the current frame, that's our next entry */
779
+ prev_entry = tree_iterator_current_entry(frame);
780
+ entry = frame->entries.contents[frame->next_idx];
781
+ frame->next_idx++;
683
782
 
684
- /* find next and load trees */
685
- if ((error = tree_iterator__set_next(ti, tf)) < 0)
686
- return error;
783
+ /* we can have collisions when iterating case insensitively. (eg,
784
+ * 'A/a' and 'a/A'). squash this one if it's already been seen.
785
+ */
786
+ if (iterator__ignore_case(&iter->base) &&
787
+ prev_entry &&
788
+ tree_iterator_entry_cmp_icase(prev_entry, entry) == 0)
789
+ continue;
687
790
 
688
- /* deal with include_trees / auto_expand as needed */
689
- if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
690
- return tree_iterator__advance_into_internal(self);
791
+ if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0)
792
+ break;
691
793
 
692
- return 0;
693
- }
794
+ /* if this path is before our start, advance over this entry */
795
+ if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false))
796
+ continue;
694
797
 
695
- static int tree_iterator__current(
696
- const git_index_entry **out, git_iterator *self)
697
- {
698
- const git_index_entry *entry = NULL;
699
- iterator_pathlist__match_t m;
700
- int error;
798
+ /* if this path is after our end, stop */
799
+ if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) {
800
+ error = GIT_ITEROVER;
801
+ break;
802
+ }
701
803
 
702
- do {
703
- if ((error = tree_iterator__current_internal(&entry, self)) < 0)
704
- return error;
804
+ /* if we have a list of paths we're interested in, examine it */
805
+ if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr))
806
+ continue;
705
807
 
706
- if (self->pathlist.length) {
707
- m = iterator_pathlist__match(
708
- self, entry->path, strlen(entry->path));
808
+ is_tree = git_tree_entry__is_tree(entry->tree_entry);
709
809
 
710
- if (m != ITERATOR_PATHLIST_MATCH) {
711
- if ((error = tree_iterator__advance_internal(self)) < 0)
712
- return error;
810
+ /* if we are *not* including trees then advance over this entry */
811
+ if (is_tree && !iterator__include_trees(iter)) {
713
812
 
714
- entry = NULL;
813
+ /* if we've found a tree (and are not returning it to the caller)
814
+ * and we are autoexpanding, then we want to return the first
815
+ * child. push the new directory and advance.
816
+ */
817
+ if (iterator__do_autoexpand(iter)) {
818
+ if ((error = tree_iterator_frame_push(iter, entry)) < 0)
819
+ break;
715
820
  }
821
+
822
+ continue;
716
823
  }
717
- } while (!entry);
824
+
825
+ tree_iterator_set_current(iter, frame, entry);
826
+
827
+ /* if we are autoexpanding, then push this as a new frame, so that
828
+ * the next call to `advance` will dive into this directory.
829
+ */
830
+ if (is_tree && iterator__do_autoexpand(iter))
831
+ error = tree_iterator_frame_push(iter, entry);
832
+
833
+ break;
834
+ }
718
835
 
719
836
  if (out)
720
- *out = entry;
837
+ *out = (error == 0) ? &iter->entry : NULL;
721
838
 
722
839
  return error;
723
840
  }
724
841
 
725
- static int tree_iterator__advance(
726
- const git_index_entry **entry, git_iterator *self)
842
+ static int tree_iterator_advance_into(
843
+ const git_index_entry **out, git_iterator *i)
727
844
  {
728
- int error = tree_iterator__advance_internal(self);
845
+ tree_iterator *iter = (tree_iterator *)i;
846
+ tree_iterator_frame *frame;
847
+ tree_iterator_entry *prev_entry;
848
+ int error;
729
849
 
730
- iterator__clear_entry(entry);
850
+ if (out)
851
+ *out = NULL;
731
852
 
732
- if (error < 0)
733
- return error;
853
+ if ((frame = tree_iterator_current_frame(iter)) == NULL)
854
+ return GIT_ITEROVER;
734
855
 
735
- return tree_iterator__current(entry, self);
736
- }
856
+ /* get the last seen entry */
857
+ prev_entry = tree_iterator_current_entry(frame);
737
858
 
738
- static int tree_iterator__advance_into(
739
- const git_index_entry **entry, git_iterator *self)
740
- {
741
- int error = tree_iterator__advance_into_internal(self);
859
+ /* it's legal to call advance_into when auto-expand is on. in this case,
860
+ * we will have pushed a new (empty) frame on to the stack for this
861
+ * new directory. since it's empty, its current_entry should be null.
862
+ */
863
+ assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
742
864
 
743
- iterator__clear_entry(entry);
865
+ if (prev_entry) {
866
+ if (!git_tree_entry__is_tree(prev_entry->tree_entry))
867
+ return 0;
744
868
 
745
- if (error < 0)
746
- return error;
869
+ if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0)
870
+ return error;
871
+ }
747
872
 
748
- return tree_iterator__current(entry, self);
873
+ /* we've advanced into the directory in question, let advance
874
+ * find the first entry
875
+ */
876
+ return tree_iterator_advance(out, i);
749
877
  }
750
878
 
751
- static int tree_iterator__seek(git_iterator *self, const char *prefix)
879
+ static int tree_iterator_advance_over(
880
+ const git_index_entry **out,
881
+ git_iterator_status_t *status,
882
+ git_iterator *i)
752
883
  {
753
- GIT_UNUSED(self); GIT_UNUSED(prefix);
754
- return -1;
884
+ *status = GIT_ITERATOR_STATUS_NORMAL;
885
+ return git_iterator_advance(out, i);
755
886
  }
756
887
 
757
- static int tree_iterator__reset(
758
- git_iterator *self, const char *start, const char *end)
888
+ static void tree_iterator_clear(tree_iterator *iter)
759
889
  {
760
- tree_iterator *ti = (tree_iterator *)self;
890
+ while (iter->frames.size)
891
+ tree_iterator_frame_pop(iter);
761
892
 
762
- tree_iterator__pop_all(ti, false, false);
893
+ git_array_clear(iter->frames);
763
894
 
764
- if (iterator__reset_range(self, start, end) < 0)
765
- return -1;
895
+ git_pool_clear(&iter->entry_pool);
896
+ git_buf_clear(&iter->entry_path);
766
897
 
767
- return tree_iterator__push_frame(ti); /* re-expand root tree */
898
+ iterator_clear(&iter->base);
768
899
  }
769
900
 
770
- static int tree_iterator__at_end(git_iterator *self)
901
+ static int tree_iterator_init(tree_iterator *iter)
771
902
  {
772
- tree_iterator *ti = (tree_iterator *)self;
773
- return (ti->head->current >= ti->head->n_entries);
774
- }
903
+ int error;
775
904
 
776
- static void tree_iterator__free(git_iterator *self)
777
- {
778
- tree_iterator *ti = (tree_iterator *)self;
905
+ git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry));
779
906
 
780
- if (ti->head) {
781
- tree_iterator__pop_all(ti, true, false);
782
- git_tree_free(ti->head->entries[0]->tree);
783
- git__free(ti->head);
784
- }
907
+ if ((error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0)
908
+ return error;
785
909
 
786
- git_pool_clear(&ti->pool);
787
- git_buf_free(&ti->path);
910
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
911
+
912
+ return 0;
788
913
  }
789
914
 
790
- static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
915
+ static int tree_iterator_reset(git_iterator *i)
791
916
  {
792
- size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
793
- tree_iterator_frame *root = git__calloc(sz, sizeof(char));
794
- GITERR_CHECK_ALLOC(root);
917
+ tree_iterator *iter = (tree_iterator *)i;
795
918
 
796
- root->n_entries = 1;
797
- root->next = 1;
798
- root->start = ti->base.start;
799
- root->startlen = root->start ? strlen(root->start) : 0;
800
- root->entries[0] = git_pool_mallocz(&ti->pool, 1);
801
- GITERR_CHECK_ALLOC(root->entries[0]);
802
- root->entries[0]->tree = tree;
919
+ tree_iterator_clear(iter);
920
+ return tree_iterator_init(iter);
921
+ }
922
+
923
+ static void tree_iterator_free(git_iterator *i)
924
+ {
925
+ tree_iterator *iter = (tree_iterator *)i;
803
926
 
804
- ti->head = ti->root = root;
927
+ tree_iterator_clear(iter);
805
928
 
806
- return 0;
929
+ git_tree_free(iter->root);
930
+ git_buf_free(&iter->entry_path);
807
931
  }
808
932
 
809
933
  int git_iterator_for_tree(
810
- git_iterator **iter,
934
+ git_iterator **out,
811
935
  git_tree *tree,
812
936
  git_iterator_options *options)
813
937
  {
938
+ tree_iterator *iter;
814
939
  int error;
815
- tree_iterator *ti;
816
-
817
- if (tree == NULL)
818
- return git_iterator_for_nothing(iter, options);
819
940
 
820
- if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
821
- return error;
941
+ static git_iterator_callbacks callbacks = {
942
+ tree_iterator_current,
943
+ tree_iterator_advance,
944
+ tree_iterator_advance_into,
945
+ tree_iterator_advance_over,
946
+ tree_iterator_reset,
947
+ tree_iterator_free
948
+ };
822
949
 
823
- ti = git__calloc(1, sizeof(tree_iterator));
824
- GITERR_CHECK_ALLOC(ti);
950
+ *out = NULL;
825
951
 
826
- ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
952
+ if (tree == NULL)
953
+ return git_iterator_for_nothing(out, options);
827
954
 
828
- if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0)
829
- goto fail;
955
+ iter = git__calloc(1, sizeof(tree_iterator));
956
+ GITERR_CHECK_ALLOC(iter);
830
957
 
831
- git_pool_init(&ti->pool, sizeof(tree_iterator_entry));
958
+ iter->base.type = GIT_ITERATOR_TYPE_TREE;
959
+ iter->base.cb = &callbacks;
832
960
 
833
- if ((error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
834
- (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
835
- goto fail;
961
+ if ((error = iterator_init_common(&iter->base,
962
+ git_tree_owner(tree), NULL, options)) < 0 ||
963
+ (error = git_tree_dup(&iter->root, tree)) < 0 ||
964
+ (error = tree_iterator_init(iter)) < 0)
965
+ goto on_error;
836
966
 
837
- *iter = (git_iterator *)ti;
967
+ *out = &iter->base;
838
968
  return 0;
839
969
 
840
- fail:
841
- git_iterator_free((git_iterator *)ti);
970
+ on_error:
971
+ git_iterator_free(&iter->base);
842
972
  return error;
843
973
  }
844
974
 
845
-
846
- typedef struct {
847
- git_iterator base;
848
- git_iterator_callbacks cb;
849
- git_index *index;
850
- git_vector entries;
851
- git_vector_cmp entry_srch;
852
- size_t current;
853
- /* when limiting with a pathlist, this is the current index into it */
854
- size_t pathlist_idx;
855
- /* when not in autoexpand mode, use these to represent "tree" state */
856
- git_buf partial;
857
- size_t partial_pos;
858
- char restore_terminator;
859
- git_index_entry tree_entry;
860
- } index_iterator;
861
-
862
- static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
863
- {
864
- const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
865
-
866
- if (ie != NULL && iterator__past_end(ii, ie->path)) {
867
- ii->current = git_vector_length(&ii->entries);
868
- ie = NULL;
869
- }
870
-
871
- return ie;
872
- }
873
-
874
- static const git_index_entry *index_iterator__advance_over_unwanted(
875
- index_iterator *ii)
975
+ int git_iterator_current_tree_entry(
976
+ const git_tree_entry **tree_entry, git_iterator *i)
876
977
  {
877
- const git_index_entry *ie = index_iterator__index_entry(ii);
878
- bool match;
879
-
880
- while (ie) {
881
- if (!iterator__include_conflicts(ii) &&
882
- git_index_entry_is_conflict(ie)) {
883
- ii->current++;
884
- ie = index_iterator__index_entry(ii);
885
- continue;
886
- }
978
+ tree_iterator *iter;
979
+ tree_iterator_frame *frame;
980
+ tree_iterator_entry *entry;
887
981
 
888
- /* if we have a pathlist, this entry's path must be in it to be
889
- * returned. walk the pathlist in unison with the index to
890
- * compare paths.
891
- */
892
- if (ii->base.pathlist.length) {
893
- match = iterator_pathlist_walk__contains(&ii->base, ie->path);
982
+ assert(i->type == GIT_ITERATOR_TYPE_TREE);
894
983
 
895
- if (!match) {
896
- ii->current++;
897
- ie = index_iterator__index_entry(ii);
898
- continue;
899
- }
900
- }
984
+ iter = (tree_iterator *)i;
901
985
 
902
- break;
903
- }
986
+ frame = tree_iterator_current_frame(iter);
987
+ entry = tree_iterator_current_entry(frame);
904
988
 
905
- return ie;
989
+ *tree_entry = entry->tree_entry;
990
+ return 0;
906
991
  }
907
992
 
908
- static void index_iterator__next_prefix_tree(index_iterator *ii)
993
+ int git_iterator_current_parent_tree(
994
+ const git_tree **parent_tree, git_iterator *i, size_t depth)
909
995
  {
910
- const char *slash;
996
+ tree_iterator *iter;
997
+ tree_iterator_frame *frame;
911
998
 
912
- if (!iterator__include_trees(ii))
913
- return;
999
+ assert(i->type == GIT_ITERATOR_TYPE_TREE);
914
1000
 
915
- slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
1001
+ iter = (tree_iterator *)i;
916
1002
 
917
- if (slash != NULL) {
918
- ii->partial_pos = (slash - ii->partial.ptr) + 1;
919
- ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
920
- ii->partial.ptr[ii->partial_pos] = '\0';
921
- } else {
922
- ii->partial_pos = ii->partial.size;
923
- }
1003
+ assert(depth < iter->frames.size);
1004
+ frame = &iter->frames.ptr[iter->frames.size-depth-1];
924
1005
 
925
- if (index_iterator__index_entry(ii) == NULL)
926
- ii->partial_pos = ii->partial.size;
1006
+ *parent_tree = frame->tree;
1007
+ return 0;
927
1008
  }
928
1009
 
929
- static int index_iterator__first_prefix_tree(index_iterator *ii)
930
- {
931
- const git_index_entry *ie = index_iterator__advance_over_unwanted(ii);
932
- const char *scan, *prior, *slash;
1010
+ /* Filesystem iterator */
933
1011
 
934
- if (!ie || !iterator__include_trees(ii))
935
- return 0;
1012
+ typedef struct {
1013
+ struct stat st;
1014
+ size_t path_len;
1015
+ iterator_pathlist_search_t match;
1016
+ char path[GIT_FLEX_ARRAY];
1017
+ } filesystem_iterator_entry;
936
1018
 
937
- /* find longest common prefix with prior index entry */
938
- for (scan = slash = ie->path, prior = ii->partial.ptr;
939
- *scan && *scan == *prior; ++scan, ++prior)
940
- if (*scan == '/')
941
- slash = scan;
1019
+ typedef struct {
1020
+ git_vector entries;
1021
+ git_pool entry_pool;
1022
+ size_t next_idx;
942
1023
 
943
- if (git_buf_sets(&ii->partial, ie->path) < 0)
944
- return -1;
1024
+ size_t path_len;
1025
+ int is_ignored;
1026
+ } filesystem_iterator_frame;
945
1027
 
946
- ii->partial_pos = (slash - ie->path) + 1;
947
- index_iterator__next_prefix_tree(ii);
1028
+ typedef struct {
1029
+ git_iterator base;
1030
+ char *root;
1031
+ size_t root_len;
948
1032
 
949
- return 0;
950
- }
1033
+ unsigned int dirload_flags;
951
1034
 
952
- #define index_iterator__at_tree(I) \
953
- (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
1035
+ git_tree *tree;
1036
+ git_index *index;
1037
+ git_vector index_snapshot;
954
1038
 
955
- static int index_iterator__current(
956
- const git_index_entry **entry, git_iterator *self)
957
- {
958
- index_iterator *ii = (index_iterator *)self;
959
- const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
1039
+ git_array_t(filesystem_iterator_frame) frames;
1040
+ git_ignores ignores;
960
1041
 
961
- if (ie != NULL && index_iterator__at_tree(ii)) {
962
- ii->tree_entry.path = ii->partial.ptr;
963
- ie = &ii->tree_entry;
964
- }
1042
+ /* info about the current entry */
1043
+ git_index_entry entry;
1044
+ git_buf current_path;
1045
+ int current_is_ignored;
965
1046
 
966
- if (entry)
967
- *entry = ie;
1047
+ /* temporary buffer for advance_over */
1048
+ git_buf tmp_buf;
1049
+ } filesystem_iterator;
968
1050
 
969
- ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
970
1051
 
971
- return (ie != NULL) ? 0 : GIT_ITEROVER;
1052
+ GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame(
1053
+ filesystem_iterator *iter)
1054
+ {
1055
+ return iter->frames.size > 1 ?
1056
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
972
1057
  }
973
1058
 
974
- static int index_iterator__at_end(git_iterator *self)
1059
+ GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame(
1060
+ filesystem_iterator *iter)
975
1061
  {
976
- index_iterator *ii = (index_iterator *)self;
977
- return (ii->current >= git_vector_length(&ii->entries));
1062
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
978
1063
  }
979
1064
 
980
- static int index_iterator__advance(
981
- const git_index_entry **entry, git_iterator *self)
1065
+ GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry(
1066
+ filesystem_iterator_frame *frame)
982
1067
  {
983
- index_iterator *ii = (index_iterator *)self;
984
- size_t entrycount = git_vector_length(&ii->entries);
985
- const git_index_entry *ie;
986
-
987
- if (!iterator__has_been_accessed(ii))
988
- return index_iterator__current(entry, self);
989
-
990
- if (index_iterator__at_tree(ii)) {
991
- if (iterator__do_autoexpand(ii)) {
992
- ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
993
- index_iterator__next_prefix_tree(ii);
994
- } else {
995
- /* advance to sibling tree (i.e. find entry with new prefix) */
996
- while (ii->current < entrycount) {
997
- ii->current++;
998
-
999
- if (!(ie = git_vector_get(&ii->entries, ii->current)) ||
1000
- ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
1001
- break;
1002
- }
1003
-
1004
- if (index_iterator__first_prefix_tree(ii) < 0)
1005
- return -1;
1006
- }
1007
- } else {
1008
- if (ii->current < entrycount)
1009
- ii->current++;
1010
-
1011
- if (index_iterator__first_prefix_tree(ii) < 0)
1012
- return -1;
1013
- }
1014
-
1015
- return index_iterator__current(entry, self);
1068
+ return frame->next_idx == 0 ?
1069
+ NULL : frame->entries.contents[frame->next_idx-1];
1016
1070
  }
1017
1071
 
1018
- static int index_iterator__advance_into(
1019
- const git_index_entry **entry, git_iterator *self)
1072
+ static int filesystem_iterator_entry_cmp(const void *_a, const void *_b)
1020
1073
  {
1021
- index_iterator *ii = (index_iterator *)self;
1022
- const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
1023
-
1024
- if (ie != NULL && index_iterator__at_tree(ii)) {
1025
- if (ii->restore_terminator)
1026
- ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
1027
- index_iterator__next_prefix_tree(ii);
1028
- }
1074
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1075
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
1029
1076
 
1030
- return index_iterator__current(entry, self);
1077
+ return git__strcmp(a->path, b->path);
1031
1078
  }
1032
1079
 
1033
- static int index_iterator__seek(git_iterator *self, const char *prefix)
1080
+ static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b)
1034
1081
  {
1035
- GIT_UNUSED(self); GIT_UNUSED(prefix);
1036
- return -1;
1082
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
1083
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
1084
+
1085
+ return git__strcasecmp(a->path, b->path);
1037
1086
  }
1038
1087
 
1039
- static int index_iterator__reset(
1040
- git_iterator *self, const char *start, const char *end)
1088
+ #define FILESYSTEM_MAX_DEPTH 100
1089
+
1090
+ /**
1091
+ * Figure out if an entry is a submodule.
1092
+ *
1093
+ * We consider it a submodule if the path is listed as a submodule in
1094
+ * either the tree or the index.
1095
+ */
1096
+ static int filesystem_iterator_is_submodule(
1097
+ bool *out, filesystem_iterator *iter, const char *path, size_t path_len)
1041
1098
  {
1042
- index_iterator *ii = (index_iterator *)self;
1043
- const git_index_entry *ie;
1099
+ bool is_submodule = false;
1100
+ int error;
1044
1101
 
1045
- if (iterator__reset_range(self, start, end) < 0)
1046
- return -1;
1102
+ *out = false;
1047
1103
 
1048
- ii->current = 0;
1104
+ /* first see if this path is a submodule in HEAD */
1105
+ if (iter->tree) {
1106
+ git_tree_entry *entry;
1049
1107
 
1050
- iterator_pathlist_walk__reset(self);
1108
+ error = git_tree_entry_bypath(&entry, iter->tree, path);
1051
1109
 
1052
- /* if we're given a start prefix, find it; if we're given a pathlist, find
1053
- * the first of those. start at the later of the two.
1054
- */
1055
- if (ii->base.start)
1056
- git_index_snapshot_find(
1057
- &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
1110
+ if (error < 0 && error != GIT_ENOTFOUND)
1111
+ return error;
1058
1112
 
1059
- if ((ie = index_iterator__advance_over_unwanted(ii)) == NULL)
1060
- return 0;
1113
+ if (!error) {
1114
+ is_submodule = (entry->attr == GIT_FILEMODE_COMMIT);
1115
+ git_tree_entry_free(entry);
1116
+ }
1117
+ }
1061
1118
 
1062
- if (git_buf_sets(&ii->partial, ie->path) < 0)
1063
- return -1;
1119
+ if (!is_submodule && iter->base.index) {
1120
+ size_t pos;
1064
1121
 
1065
- ii->partial_pos = 0;
1122
+ error = git_index_snapshot_find(&pos,
1123
+ &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0);
1066
1124
 
1067
- if (ii->base.start) {
1068
- size_t startlen = strlen(ii->base.start);
1125
+ if (error < 0 && error != GIT_ENOTFOUND)
1126
+ return error;
1069
1127
 
1070
- ii->partial_pos = (startlen > ii->partial.size) ?
1071
- ii->partial.size : startlen;
1128
+ if (!error) {
1129
+ git_index_entry *e = git_vector_get(&iter->index_snapshot, pos);
1130
+ is_submodule = (e->mode == GIT_FILEMODE_COMMIT);
1131
+ }
1072
1132
  }
1073
1133
 
1074
- index_iterator__next_prefix_tree(ii);
1075
-
1134
+ *out = is_submodule;
1076
1135
  return 0;
1077
1136
  }
1078
1137
 
1079
- static void index_iterator__free(git_iterator *self)
1138
+ static void filesystem_iterator_frame_push_ignores(
1139
+ filesystem_iterator *iter,
1140
+ filesystem_iterator_entry *frame_entry,
1141
+ filesystem_iterator_frame *new_frame)
1080
1142
  {
1081
- index_iterator *ii = (index_iterator *)self;
1082
- git_index_snapshot_release(&ii->entries, ii->index);
1083
- ii->index = NULL;
1084
- git_buf_free(&ii->partial);
1085
- }
1143
+ filesystem_iterator_frame *previous_frame;
1144
+ const char *path = frame_entry ? frame_entry->path : "";
1086
1145
 
1087
- int git_iterator_for_index(
1088
- git_iterator **iter,
1089
- git_repository *repo,
1090
- git_index *index,
1091
- git_iterator_options *options)
1092
- {
1093
- int error = 0;
1094
- index_iterator *ii = git__calloc(1, sizeof(index_iterator));
1095
- GITERR_CHECK_ALLOC(ii);
1146
+ if (!iterator__honor_ignores(&iter->base))
1147
+ return;
1096
1148
 
1097
- if ((error = git_index_snapshot_new(&ii->entries, index)) < 0) {
1098
- git__free(ii);
1099
- return error;
1149
+ if (git_ignore__lookup(&new_frame->is_ignored,
1150
+ &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) {
1151
+ giterr_clear();
1152
+ new_frame->is_ignored = GIT_IGNORE_NOTFOUND;
1100
1153
  }
1101
- ii->index = index;
1102
1154
 
1103
- ITERATOR_BASE_INIT(ii, index, INDEX, repo);
1104
-
1105
- if ((error = iterator__update_ignore_case((git_iterator *)ii, options ? options->flags : 0)) < 0) {
1106
- git_iterator_free((git_iterator *)ii);
1107
- return error;
1108
- }
1155
+ /* if this is not the top level directory... */
1156
+ if (frame_entry) {
1157
+ const char *relative_path;
1109
1158
 
1110
- ii->entry_srch = iterator__ignore_case(ii) ?
1111
- git_index_entry_isrch : git_index_entry_srch;
1159
+ previous_frame = filesystem_iterator_parent_frame(iter);
1112
1160
 
1113
- git_vector_set_cmp(&ii->entries, iterator__ignore_case(ii) ?
1114
- git_index_entry_icmp : git_index_entry_cmp);
1115
- git_vector_sort(&ii->entries);
1161
+ /* push new ignores for files in this directory */
1162
+ relative_path = frame_entry->path + previous_frame->path_len;
1116
1163
 
1117
- git_buf_init(&ii->partial, 0);
1118
- ii->tree_entry.mode = GIT_FILEMODE_TREE;
1164
+ /* inherit ignored from parent if no rule specified */
1165
+ if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND)
1166
+ new_frame->is_ignored = previous_frame->is_ignored;
1119
1167
 
1120
- index_iterator__reset((git_iterator *)ii, NULL, NULL);
1168
+ git_ignore__push_dir(&iter->ignores, relative_path);
1169
+ }
1170
+ }
1121
1171
 
1122
- *iter = (git_iterator *)ii;
1123
- return 0;
1172
+ static void filesystem_iterator_frame_pop_ignores(
1173
+ filesystem_iterator *iter)
1174
+ {
1175
+ if (iterator__honor_ignores(&iter->base))
1176
+ git_ignore__pop_dir(&iter->ignores);
1124
1177
  }
1125
1178
 
1179
+ GIT_INLINE(bool) filesystem_iterator_examine_path(
1180
+ bool *is_dir_out,
1181
+ iterator_pathlist_search_t *match_out,
1182
+ filesystem_iterator *iter,
1183
+ filesystem_iterator_entry *frame_entry,
1184
+ const char *path,
1185
+ size_t path_len)
1186
+ {
1187
+ bool is_dir = 0;
1188
+ iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL;
1126
1189
 
1127
- typedef struct fs_iterator_frame fs_iterator_frame;
1128
- struct fs_iterator_frame {
1129
- fs_iterator_frame *next;
1130
- git_vector entries;
1131
- size_t index;
1132
- int is_ignored;
1133
- };
1190
+ *is_dir_out = false;
1191
+ *match_out = ITERATOR_PATHLIST_NONE;
1134
1192
 
1135
- typedef struct fs_iterator fs_iterator;
1136
- struct fs_iterator {
1137
- git_iterator base;
1138
- git_iterator_callbacks cb;
1139
- fs_iterator_frame *stack;
1140
- git_index_entry entry;
1141
- git_buf path;
1142
- size_t root_len;
1143
- uint32_t dirload_flags;
1144
- int depth;
1145
- iterator_pathlist__match_t pathlist_match;
1193
+ if (iter->base.start_len) {
1194
+ int cmp = iter->base.strncomp(path, iter->base.start, path_len);
1146
1195
 
1147
- int (*enter_dir_cb)(fs_iterator *self);
1148
- int (*leave_dir_cb)(fs_iterator *self);
1149
- int (*update_entry_cb)(fs_iterator *self);
1150
- };
1196
+ /* we haven't stat'ed `path` yet, so we don't yet know if it's a
1197
+ * directory or not. special case if the current path may be a
1198
+ * directory that matches the start prefix.
1199
+ */
1200
+ if (cmp == 0) {
1201
+ if (iter->base.start[path_len] == '/')
1202
+ is_dir = true;
1151
1203
 
1152
- #define FS_MAX_DEPTH 100
1204
+ else if (iter->base.start[path_len] != '\0')
1205
+ cmp = -1;
1206
+ }
1153
1207
 
1154
- typedef struct {
1155
- struct stat st;
1156
- iterator_pathlist__match_t pathlist_match;
1157
- size_t path_len;
1158
- char path[GIT_FLEX_ARRAY];
1159
- } fs_iterator_path_with_stat;
1208
+ if (cmp < 0)
1209
+ return false;
1210
+ }
1160
1211
 
1161
- static int fs_iterator_path_with_stat_cmp(const void *a, const void *b)
1162
- {
1163
- const fs_iterator_path_with_stat *psa = a, *psb = b;
1164
- return strcmp(psa->path, psb->path);
1165
- }
1212
+ if (iter->base.end_len) {
1213
+ int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len);
1166
1214
 
1167
- static int fs_iterator_path_with_stat_cmp_icase(const void *a, const void *b)
1168
- {
1169
- const fs_iterator_path_with_stat *psa = a, *psb = b;
1170
- return strcasecmp(psa->path, psb->path);
1171
- }
1215
+ if (cmp > 0)
1216
+ return false;
1217
+ }
1172
1218
 
1173
- static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
1174
- {
1175
- fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
1176
- git_vector_cmp entry_compare = CASESELECT(
1177
- iterator__ignore_case(fi),
1178
- fs_iterator_path_with_stat_cmp_icase,
1179
- fs_iterator_path_with_stat_cmp);
1219
+ /* if we have a pathlist that we're limiting to, examine this path now
1220
+ * to avoid a `stat` if we're not interested in the path.
1221
+ */
1222
+ if (iter->base.pathlist.length) {
1223
+ /* if our parent was explicitly included, so too are we */
1224
+ if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT)
1225
+ match = ITERATOR_PATHLIST_FULL;
1226
+ else
1227
+ match = iterator_pathlist_search(&iter->base, path, path_len);
1180
1228
 
1181
- if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
1182
- git__free(ff);
1183
- ff = NULL;
1184
- }
1229
+ if (match == ITERATOR_PATHLIST_NONE)
1230
+ return false;
1185
1231
 
1186
- return ff;
1187
- }
1232
+ /* Ensure that the pathlist entry lines up with what we expected */
1233
+ if (match == ITERATOR_PATHLIST_IS_DIR ||
1234
+ match == ITERATOR_PATHLIST_IS_PARENT)
1235
+ is_dir = true;
1236
+ }
1188
1237
 
1189
- static void fs_iterator__free_frame(fs_iterator_frame *ff)
1190
- {
1191
- git_vector_free_deep(&ff->entries);
1192
- git__free(ff);
1238
+ *is_dir_out = is_dir;
1239
+ *match_out = match;
1240
+ return true;
1193
1241
  }
1194
1242
 
1195
- static void fs_iterator__pop_frame(
1196
- fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
1243
+ GIT_INLINE(bool) filesystem_iterator_is_dot_git(
1244
+ filesystem_iterator *iter, const char *path, size_t path_len)
1197
1245
  {
1198
- if (fi && fi->stack == ff) {
1199
- if (!ff->next && !pop_last) {
1200
- memset(&fi->entry, 0, sizeof(fi->entry));
1201
- return;
1202
- }
1246
+ size_t len;
1203
1247
 
1204
- if (fi->leave_dir_cb)
1205
- (void)fi->leave_dir_cb(fi);
1248
+ if (!iterator__ignore_dot_git(&iter->base))
1249
+ return false;
1206
1250
 
1207
- fi->stack = ff->next;
1208
- fi->depth--;
1209
- }
1251
+ if ((len = path_len) < 4)
1252
+ return false;
1210
1253
 
1211
- fs_iterator__free_frame(ff);
1212
- }
1254
+ if (path[len - 1] == '/')
1255
+ len--;
1213
1256
 
1214
- static int fs_iterator__update_entry(fs_iterator *fi);
1215
- static int fs_iterator__advance_over(
1216
- const git_index_entry **entry, git_iterator *self);
1257
+ if (git__tolower(path[len - 1]) != 't' ||
1258
+ git__tolower(path[len - 2]) != 'i' ||
1259
+ git__tolower(path[len - 3]) != 'g' ||
1260
+ git__tolower(path[len - 4]) != '.')
1261
+ return false;
1217
1262
 
1218
- static int fs_iterator__entry_cmp(const void *i, const void *item)
1219
- {
1220
- const fs_iterator *fi = (const fs_iterator *)i;
1221
- const fs_iterator_path_with_stat *ps = item;
1222
- return fi->base.prefixcomp(fi->base.start, ps->path);
1263
+ return (len == 4 || path[len - 5] == '/');
1223
1264
  }
1224
1265
 
1225
- static void fs_iterator__seek_frame_start(
1226
- fs_iterator *fi, fs_iterator_frame *ff)
1266
+ static filesystem_iterator_entry *filesystem_iterator_entry_init(
1267
+ filesystem_iterator_frame *frame,
1268
+ const char *path,
1269
+ size_t path_len,
1270
+ struct stat *statbuf,
1271
+ iterator_pathlist_search_t pathlist_match)
1227
1272
  {
1228
- if (!ff)
1229
- return;
1273
+ filesystem_iterator_entry *entry;
1274
+ size_t entry_size;
1230
1275
 
1231
- if (fi->base.start)
1232
- git_vector_bsearch2(
1233
- &ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
1234
- else
1235
- ff->index = 0;
1276
+ /* Make sure to append two bytes, one for the path's null
1277
+ * termination, one for a possible trailing '/' for folders.
1278
+ */
1279
+ if (GIT_ADD_SIZET_OVERFLOW(&entry_size,
1280
+ sizeof(filesystem_iterator_entry), path_len) ||
1281
+ GIT_ADD_SIZET_OVERFLOW(&entry_size, entry_size, 2) ||
1282
+ (entry = git_pool_malloc(&frame->entry_pool, entry_size)) == NULL)
1283
+ return NULL;
1284
+
1285
+ entry->path_len = path_len;
1286
+ entry->match = pathlist_match;
1287
+ memcpy(entry->path, path, path_len);
1288
+ memcpy(&entry->st, statbuf, sizeof(struct stat));
1289
+
1290
+ /* Suffix directory paths with a '/' */
1291
+ if (S_ISDIR(entry->st.st_mode))
1292
+ entry->path[entry->path_len++] = '/';
1293
+
1294
+ entry->path[entry->path_len] = '\0';
1295
+
1296
+ return entry;
1236
1297
  }
1237
1298
 
1238
- static int dirload_with_stat(git_vector *contents, fs_iterator *fi)
1299
+ static int filesystem_iterator_frame_push(
1300
+ filesystem_iterator *iter,
1301
+ filesystem_iterator_entry *frame_entry)
1239
1302
  {
1303
+ filesystem_iterator_frame *new_frame = NULL;
1240
1304
  git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
1305
+ git_buf root = GIT_BUF_INIT;
1241
1306
  const char *path;
1242
- size_t start_len = fi->base.start ? strlen(fi->base.start) : 0;
1243
- size_t end_len = fi->base.end ? strlen(fi->base.end) : 0;
1244
- fs_iterator_path_with_stat *ps;
1245
- size_t path_len, cmp_len, ps_size;
1246
- iterator_pathlist__match_t pathlist_match = ITERATOR_PATHLIST_MATCH;
1307
+ filesystem_iterator_entry *entry;
1308
+ struct stat statbuf;
1309
+ size_t path_len;
1247
1310
  int error;
1248
1311
 
1312
+ if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
1313
+ giterr_set(GITERR_REPOSITORY,
1314
+ "directory nesting too deep (%d)", iter->frames.size);
1315
+ return -1;
1316
+ }
1317
+
1318
+ new_frame = git_array_alloc(iter->frames);
1319
+ GITERR_CHECK_ALLOC(new_frame);
1320
+
1321
+ memset(new_frame, 0, sizeof(filesystem_iterator_frame));
1322
+
1323
+ if (frame_entry)
1324
+ git_buf_joinpath(&root, iter->root, frame_entry->path);
1325
+ else
1326
+ git_buf_puts(&root, iter->root);
1327
+
1328
+ if (git_buf_oom(&root)) {
1329
+ error = -1;
1330
+ goto done;
1331
+ }
1332
+
1333
+ new_frame->path_len = frame_entry ? frame_entry->path_len : 0;
1334
+
1249
1335
  /* Any error here is equivalent to the dir not existing, skip over it */
1250
1336
  if ((error = git_path_diriter_init(
1251
- &diriter, fi->path.ptr, fi->dirload_flags)) < 0) {
1337
+ &diriter, root.ptr, iter->dirload_flags)) < 0) {
1252
1338
  error = GIT_ENOTFOUND;
1253
1339
  goto done;
1254
1340
  }
1255
1341
 
1342
+ if ((error = git_vector_init(&new_frame->entries, 64,
1343
+ iterator__ignore_case(&iter->base) ?
1344
+ filesystem_iterator_entry_cmp_icase :
1345
+ filesystem_iterator_entry_cmp)) < 0)
1346
+ goto done;
1347
+
1348
+ git_pool_init(&new_frame->entry_pool, 1);
1349
+
1350
+ /* check if this directory is ignored */
1351
+ filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame);
1352
+
1256
1353
  while ((error = git_path_diriter_next(&diriter)) == 0) {
1354
+ iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
1355
+ bool dir_expected = false;
1356
+
1257
1357
  if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
1258
1358
  goto done;
1259
1359
 
1260
- assert(path_len > fi->root_len);
1360
+ assert(path_len > iter->root_len);
1261
1361
 
1262
1362
  /* remove the prefix if requested */
1263
- path += fi->root_len;
1264
- path_len -= fi->root_len;
1265
-
1266
- /* skip if before start_stat or after end_stat */
1267
- cmp_len = min(start_len, path_len);
1268
- if (cmp_len && fi->base.strncomp(path, fi->base.start, cmp_len) < 0)
1269
- continue;
1270
- /* skip if after end_stat */
1271
- cmp_len = min(end_len, path_len);
1272
- if (cmp_len && fi->base.strncomp(path, fi->base.end, cmp_len) > 0)
1273
- continue;
1363
+ path += iter->root_len;
1364
+ path_len -= iter->root_len;
1274
1365
 
1275
- /* if we have a pathlist that we're limiting to, examine this path.
1276
- * if the frame has already deemed us inside the path (eg, we're in
1277
- * `foo/bar` and the pathlist previously was detected to say `foo/`)
1278
- * then simply continue. otherwise, examine the pathlist looking for
1279
- * this path or children of this path.
1366
+ /* examine start / end and the pathlist to see if this path is in it.
1367
+ * note that since we haven't yet stat'ed the path, we cannot know
1368
+ * whether it's a directory yet or not, so this can give us an
1369
+ * expected type (S_IFDIR or S_IFREG) that we should examine)
1280
1370
  */
1281
- if (fi->base.pathlist.length &&
1282
- fi->pathlist_match != ITERATOR_PATHLIST_MATCH &&
1283
- fi->pathlist_match != ITERATOR_PATHLIST_MATCH_DIRECTORY &&
1284
- !(pathlist_match = iterator_pathlist__match(&fi->base, path, path_len)))
1371
+ if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match,
1372
+ iter, frame_entry, path, path_len))
1285
1373
  continue;
1286
1374
 
1287
- /* Make sure to append two bytes, one for the path's null
1288
- * termination, one for a possible trailing '/' for folders.
1375
+ /* TODO: don't need to stat if assume unchanged for this path and
1376
+ * we have an index, we can just copy the data out of it.
1289
1377
  */
1290
- GITERR_CHECK_ALLOC_ADD(&ps_size, sizeof(fs_iterator_path_with_stat), path_len);
1291
- GITERR_CHECK_ALLOC_ADD(&ps_size, ps_size, 2);
1292
1378
 
1293
- ps = git__calloc(1, ps_size);
1294
- ps->path_len = path_len;
1379
+ if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) {
1380
+ /* file was removed between readdir and lstat */
1381
+ if (error == GIT_ENOTFOUND)
1382
+ continue;
1295
1383
 
1296
- memcpy(ps->path, path, path_len);
1384
+ /* treat the file as unreadable */
1385
+ memset(&statbuf, 0, sizeof(statbuf));
1386
+ statbuf.st_mode = GIT_FILEMODE_UNREADABLE;
1297
1387
 
1298
- /* TODO: don't stat if assume unchanged for this path */
1388
+ error = 0;
1389
+ }
1299
1390
 
1300
- if ((error = git_path_diriter_stat(&ps->st, &diriter)) < 0) {
1301
- if (error == GIT_ENOTFOUND) {
1302
- /* file was removed between readdir and lstat */
1303
- git__free(ps);
1304
- continue;
1305
- }
1391
+ iter->base.stat_calls++;
1306
1392
 
1307
- if (pathlist_match == ITERATOR_PATHLIST_MATCH_DIRECTORY) {
1308
- /* were looking for a directory, but this is a file */
1309
- git__free(ps);
1310
- continue;
1311
- }
1312
-
1313
- /* Treat the file as unreadable if we get any other error */
1314
- memset(&ps->st, 0, sizeof(ps->st));
1315
- ps->st.st_mode = GIT_FILEMODE_UNREADABLE;
1393
+ /* Ignore wacky things in the filesystem */
1394
+ if (!S_ISDIR(statbuf.st_mode) &&
1395
+ !S_ISREG(statbuf.st_mode) &&
1396
+ !S_ISLNK(statbuf.st_mode) &&
1397
+ statbuf.st_mode != GIT_FILEMODE_UNREADABLE)
1398
+ continue;
1316
1399
 
1317
- giterr_clear();
1318
- error = 0;
1319
- } else if (S_ISDIR(ps->st.st_mode)) {
1320
- /* Suffix directory paths with a '/' */
1321
- ps->path[ps->path_len++] = '/';
1322
- ps->path[ps->path_len] = '\0';
1323
- } else if(!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) {
1324
- /* Ignore wacky things in the filesystem */
1325
- git__free(ps);
1400
+ if (filesystem_iterator_is_dot_git(iter, path, path_len))
1326
1401
  continue;
1402
+
1403
+ /* convert submodules to GITLINK and remove trailing slashes */
1404
+ if (S_ISDIR(statbuf.st_mode)) {
1405
+ bool submodule = false;
1406
+
1407
+ if ((error = filesystem_iterator_is_submodule(&submodule,
1408
+ iter, path, path_len)) < 0)
1409
+ goto done;
1410
+
1411
+ if (submodule)
1412
+ statbuf.st_mode = GIT_FILEMODE_COMMIT;
1327
1413
  }
1328
1414
 
1329
- /* record whether this path was explicitly found in the path list
1330
- * or whether we're only examining it because something beneath it
1331
- * is in the path list.
1332
- */
1333
- ps->pathlist_match = pathlist_match;
1334
- git_vector_insert(contents, ps);
1415
+ /* Ensure that the pathlist entry lines up with what we expected */
1416
+ else if (dir_expected)
1417
+ continue;
1418
+
1419
+ entry = filesystem_iterator_entry_init(new_frame,
1420
+ path, path_len, &statbuf, pathlist_match);
1421
+ GITERR_CHECK_ALLOC(entry);
1422
+
1423
+ git_vector_insert(&new_frame->entries, entry);
1335
1424
  }
1336
1425
 
1337
1426
  if (error == GIT_ITEROVER)
1338
1427
  error = 0;
1339
1428
 
1340
1429
  /* sort now that directory suffix is added */
1341
- git_vector_sort(contents);
1430
+ git_vector_sort(&new_frame->entries);
1342
1431
 
1343
1432
  done:
1433
+ if (error < 0)
1434
+ git_array_pop(iter->frames);
1435
+
1436
+ git_buf_free(&root);
1344
1437
  git_path_diriter_free(&diriter);
1345
1438
  return error;
1346
1439
  }
1347
1440
 
1348
-
1349
- static int fs_iterator__expand_dir(fs_iterator *fi)
1441
+ GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter)
1350
1442
  {
1351
- int error;
1352
- fs_iterator_frame *ff;
1353
-
1354
- if (fi->depth > FS_MAX_DEPTH) {
1355
- giterr_set(GITERR_REPOSITORY,
1356
- "Directory nesting is too deep (%d)", fi->depth);
1357
- return -1;
1358
- }
1359
-
1360
- ff = fs_iterator__alloc_frame(fi);
1361
- GITERR_CHECK_ALLOC(ff);
1443
+ filesystem_iterator_frame *frame;
1362
1444
 
1363
- error = dirload_with_stat(&ff->entries, fi);
1364
-
1365
- if (error < 0) {
1366
- git_error_state last_error = { 0 };
1367
- giterr_state_capture(&last_error, error);
1445
+ assert(iter->frames.size);
1368
1446
 
1369
- /* these callbacks may clear the error message */
1370
- fs_iterator__free_frame(ff);
1371
- fs_iterator__advance_over(NULL, (git_iterator *)fi);
1372
- /* next time return value we skipped to */
1373
- fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1447
+ frame = git_array_pop(iter->frames);
1448
+ filesystem_iterator_frame_pop_ignores(iter);
1374
1449
 
1375
- return giterr_state_restore(&last_error);
1376
- }
1450
+ git_pool_clear(&frame->entry_pool);
1451
+ git_vector_free(&frame->entries);
1452
+ }
1377
1453
 
1378
- if (ff->entries.length == 0) {
1379
- fs_iterator__free_frame(ff);
1380
- return GIT_ENOTFOUND;
1381
- }
1382
- fi->base.stat_calls += ff->entries.length;
1454
+ static void filesystem_iterator_set_current(
1455
+ filesystem_iterator *iter,
1456
+ filesystem_iterator_entry *entry)
1457
+ {
1458
+ iter->entry.ctime.seconds = entry->st.st_ctime;
1459
+ iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
1383
1460
 
1384
- fs_iterator__seek_frame_start(fi, ff);
1461
+ iter->entry.mtime.seconds = entry->st.st_mtime;
1462
+ iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
1385
1463
 
1386
- ff->next = fi->stack;
1387
- fi->stack = ff;
1388
- fi->depth++;
1464
+ iter->entry.dev = entry->st.st_dev;
1465
+ iter->entry.ino = entry->st.st_ino;
1466
+ iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode);
1467
+ iter->entry.uid = entry->st.st_uid;
1468
+ iter->entry.gid = entry->st.st_gid;
1469
+ iter->entry.file_size = entry->st.st_size;
1389
1470
 
1390
- if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
1391
- return error;
1471
+ iter->entry.path = entry->path;
1392
1472
 
1393
- return fs_iterator__update_entry(fi);
1473
+ iter->current_is_ignored = GIT_IGNORE_UNCHECKED;
1394
1474
  }
1395
1475
 
1396
- static int fs_iterator__current(
1397
- const git_index_entry **entry, git_iterator *self)
1476
+ static int filesystem_iterator_current(
1477
+ const git_index_entry **out, git_iterator *i)
1398
1478
  {
1399
- fs_iterator *fi = (fs_iterator *)self;
1400
- const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
1479
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1401
1480
 
1402
- if (entry)
1403
- *entry = fe;
1481
+ if (!iterator__has_been_accessed(i))
1482
+ return iter->base.cb->advance(out, i);
1404
1483
 
1405
- fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1406
-
1407
- return (fe != NULL) ? 0 : GIT_ITEROVER;
1408
- }
1484
+ if (!iter->frames.size) {
1485
+ *out = NULL;
1486
+ return GIT_ITEROVER;
1487
+ }
1409
1488
 
1410
- static int fs_iterator__at_end(git_iterator *self)
1411
- {
1412
- return (((fs_iterator *)self)->entry.path == NULL);
1489
+ *out = &iter->entry;
1490
+ return 0;
1413
1491
  }
1414
1492
 
1415
- static int fs_iterator__advance_into(
1416
- const git_index_entry **entry, git_iterator *iter)
1493
+ static int filesystem_iterator_advance(
1494
+ const git_index_entry **out, git_iterator *i)
1417
1495
  {
1496
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1418
1497
  int error = 0;
1419
- fs_iterator *fi = (fs_iterator *)iter;
1420
1498
 
1421
- iterator__clear_entry(entry);
1499
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1422
1500
 
1423
- /* Allow you to explicitly advance into a commit/submodule (as well as a
1424
- * tree) to avoid cases where an entry is mislabeled as a submodule in
1425
- * the working directory. The fs iterator will never have COMMMIT
1426
- * entries on it's own, but a wrapper might add them.
1427
- */
1428
- if (fi->entry.path != NULL &&
1429
- (fi->entry.mode == GIT_FILEMODE_TREE ||
1430
- fi->entry.mode == GIT_FILEMODE_COMMIT))
1431
- /* returns GIT_ENOTFOUND if the directory is empty */
1432
- error = fs_iterator__expand_dir(fi);
1501
+ /* examine filesystem entries until we find the next one to return */
1502
+ while (true) {
1503
+ filesystem_iterator_frame *frame;
1504
+ filesystem_iterator_entry *entry;
1433
1505
 
1434
- if (!error && entry)
1435
- error = fs_iterator__current(entry, iter);
1506
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL) {
1507
+ error = GIT_ITEROVER;
1508
+ break;
1509
+ }
1436
1510
 
1437
- if (!error && !fi->entry.path)
1438
- error = GIT_ITEROVER;
1511
+ /* no more entries in this frame. pop the frame out */
1512
+ if (frame->next_idx == frame->entries.length) {
1513
+ filesystem_iterator_frame_pop(iter);
1514
+ continue;
1515
+ }
1439
1516
 
1440
- return error;
1441
- }
1517
+ /* we have more entries in the current frame, that's our next entry */
1518
+ entry = frame->entries.contents[frame->next_idx];
1519
+ frame->next_idx++;
1442
1520
 
1443
- static void fs_iterator__advance_over_internal(git_iterator *self)
1444
- {
1445
- fs_iterator *fi = (fs_iterator *)self;
1446
- fs_iterator_frame *ff;
1447
- fs_iterator_path_with_stat *next;
1521
+ if (S_ISDIR(entry->st.st_mode)) {
1522
+ if (iterator__do_autoexpand(iter)) {
1523
+ error = filesystem_iterator_frame_push(iter, entry);
1448
1524
 
1449
- while (fi->entry.path != NULL) {
1450
- ff = fi->stack;
1451
- next = git_vector_get(&ff->entries, ++ff->index);
1525
+ /* may get GIT_ENOTFOUND due to races or permission problems
1526
+ * that we want to quietly swallow
1527
+ */
1528
+ if (error == GIT_ENOTFOUND)
1529
+ continue;
1530
+ else if (error < 0)
1531
+ break;
1532
+ }
1452
1533
 
1453
- if (next != NULL)
1454
- break;
1534
+ if (!iterator__include_trees(iter))
1535
+ continue;
1536
+ }
1455
1537
 
1456
- fs_iterator__pop_frame(fi, ff, false);
1538
+ filesystem_iterator_set_current(iter, entry);
1539
+ break;
1457
1540
  }
1541
+
1542
+ if (out)
1543
+ *out = (error == 0) ? &iter->entry : NULL;
1544
+
1545
+ return error;
1458
1546
  }
1459
1547
 
1460
- static int fs_iterator__advance_over(
1461
- const git_index_entry **entry, git_iterator *self)
1548
+ static int filesystem_iterator_advance_into(
1549
+ const git_index_entry **out, git_iterator *i)
1462
1550
  {
1551
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1552
+ filesystem_iterator_frame *frame;
1553
+ filesystem_iterator_entry *prev_entry;
1463
1554
  int error;
1464
1555
 
1465
- if (entry != NULL)
1466
- *entry = NULL;
1556
+ if (out)
1557
+ *out = NULL;
1558
+
1559
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL)
1560
+ return GIT_ITEROVER;
1561
+
1562
+ /* get the last seen entry */
1563
+ prev_entry = filesystem_iterator_current_entry(frame);
1467
1564
 
1468
- fs_iterator__advance_over_internal(self);
1565
+ /* it's legal to call advance_into when auto-expand is on. in this case,
1566
+ * we will have pushed a new (empty) frame on to the stack for this
1567
+ * new directory. since it's empty, its current_entry should be null.
1568
+ */
1569
+ assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
1469
1570
 
1470
- error = fs_iterator__update_entry((fs_iterator *)self);
1571
+ if (prev_entry) {
1572
+ if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
1573
+ !S_ISDIR(prev_entry->st.st_mode))
1574
+ return 0;
1471
1575
 
1472
- if (!error && entry != NULL)
1473
- error = fs_iterator__current(entry, self);
1576
+ if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0)
1577
+ return error;
1578
+ }
1474
1579
 
1475
- return error;
1580
+ /* we've advanced into the directory in question, let advance
1581
+ * find the first entry
1582
+ */
1583
+ return filesystem_iterator_advance(out, i);
1476
1584
  }
1477
1585
 
1478
- static int fs_iterator__advance(
1479
- const git_index_entry **entry, git_iterator *self)
1586
+ int git_iterator_current_workdir_path(git_buf **out, git_iterator *i)
1480
1587
  {
1481
- fs_iterator *fi = (fs_iterator *)self;
1482
-
1483
- if (!iterator__has_been_accessed(fi))
1484
- return fs_iterator__current(entry, self);
1588
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1589
+ const git_index_entry *entry;
1485
1590
 
1486
- /* given include_trees & autoexpand, we might have to go into a tree */
1487
- if (iterator__do_autoexpand(fi) &&
1488
- fi->entry.path != NULL &&
1489
- fi->entry.mode == GIT_FILEMODE_TREE)
1490
- {
1491
- int error = fs_iterator__advance_into(entry, self);
1492
- if (error != GIT_ENOTFOUND)
1493
- return error;
1494
- /* continue silently past empty directories if autoexpanding */
1495
- giterr_clear();
1591
+ if (i->type != GIT_ITERATOR_TYPE_FS &&
1592
+ i->type != GIT_ITERATOR_TYPE_WORKDIR) {
1593
+ *out = NULL;
1594
+ return 0;
1496
1595
  }
1497
1596
 
1498
- return fs_iterator__advance_over(entry, self);
1597
+ git_buf_truncate(&iter->current_path, iter->root_len);
1598
+
1599
+ if (git_iterator_current(&entry, i) < 0 ||
1600
+ git_buf_puts(&iter->current_path, entry->path) < 0)
1601
+ return -1;
1602
+
1603
+ *out = &iter->current_path;
1604
+ return 0;
1499
1605
  }
1500
1606
 
1501
- static int fs_iterator__seek(git_iterator *self, const char *prefix)
1607
+ GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry)
1502
1608
  {
1503
- GIT_UNUSED(self);
1504
- GIT_UNUSED(prefix);
1505
- /* pop stack until matching prefix */
1506
- /* find prefix item in current frame */
1507
- /* push subdirectories as deep as possible while matching */
1508
- return 0;
1609
+ #if defined(GIT_WIN32) && !defined(__MINGW32__)
1610
+ return (entry && entry->mode) ?
1611
+ (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) :
1612
+ GIT_DIR_FLAG_UNKNOWN;
1613
+ #else
1614
+ GIT_UNUSED(entry);
1615
+ return GIT_DIR_FLAG_UNKNOWN;
1616
+ #endif
1509
1617
  }
1510
1618
 
1511
- static int fs_iterator__reset(
1512
- git_iterator *self, const char *start, const char *end)
1619
+ static void filesystem_iterator_update_ignored(filesystem_iterator *iter)
1513
1620
  {
1514
- int error;
1515
- fs_iterator *fi = (fs_iterator *)self;
1621
+ filesystem_iterator_frame *frame;
1622
+ git_dir_flag dir_flag = entry_dir_flag(&iter->entry);
1516
1623
 
1517
- while (fi->stack != NULL && fi->stack->next != NULL)
1518
- fs_iterator__pop_frame(fi, fi->stack, false);
1519
- fi->depth = 0;
1624
+ if (git_ignore__lookup(&iter->current_is_ignored,
1625
+ &iter->ignores, iter->entry.path, dir_flag) < 0) {
1626
+ giterr_clear();
1627
+ iter->current_is_ignored = GIT_IGNORE_NOTFOUND;
1628
+ }
1520
1629
 
1521
- if ((error = iterator__reset_range(self, start, end)) < 0)
1522
- return error;
1630
+ /* use ignore from containing frame stack */
1631
+ if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) {
1632
+ frame = filesystem_iterator_current_frame(iter);
1633
+ iter->current_is_ignored = frame->is_ignored;
1634
+ }
1635
+ }
1523
1636
 
1524
- fs_iterator__seek_frame_start(fi, fi->stack);
1637
+ GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
1638
+ filesystem_iterator *iter)
1639
+ {
1640
+ if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED)
1641
+ filesystem_iterator_update_ignored(iter);
1525
1642
 
1526
- error = fs_iterator__update_entry(fi);
1527
- if (error == GIT_ITEROVER)
1528
- error = 0;
1643
+ return (iter->current_is_ignored == GIT_IGNORE_TRUE);
1644
+ }
1529
1645
 
1530
- return error;
1646
+ bool git_iterator_current_is_ignored(git_iterator *i)
1647
+ {
1648
+ if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
1649
+ return false;
1650
+
1651
+ return filesystem_iterator_current_is_ignored((filesystem_iterator *)i);
1531
1652
  }
1532
1653
 
1533
- static void fs_iterator__free(git_iterator *self)
1654
+ bool git_iterator_current_tree_is_ignored(git_iterator *i)
1534
1655
  {
1535
- fs_iterator *fi = (fs_iterator *)self;
1656
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1657
+ filesystem_iterator_frame *frame;
1536
1658
 
1537
- while (fi->stack != NULL)
1538
- fs_iterator__pop_frame(fi, fi->stack, true);
1659
+ if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
1660
+ return false;
1539
1661
 
1540
- git_buf_free(&fi->path);
1662
+ frame = filesystem_iterator_current_frame(iter);
1663
+ return (frame->is_ignored == GIT_IGNORE_TRUE);
1541
1664
  }
1542
1665
 
1543
- static int fs_iterator__update_entry(fs_iterator *fi)
1666
+ static int filesystem_iterator_advance_over(
1667
+ const git_index_entry **out,
1668
+ git_iterator_status_t *status,
1669
+ git_iterator *i)
1544
1670
  {
1545
- fs_iterator_path_with_stat *ps;
1671
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1672
+ filesystem_iterator_frame *current_frame;
1673
+ filesystem_iterator_entry *current_entry;
1674
+ const git_index_entry *entry = NULL;
1675
+ const char *base;
1676
+ int error = 0;
1546
1677
 
1547
- while (true) {
1548
- memset(&fi->entry, 0, sizeof(fi->entry));
1678
+ *out = NULL;
1679
+ *status = GIT_ITERATOR_STATUS_NORMAL;
1549
1680
 
1550
- if (!fi->stack)
1551
- return GIT_ITEROVER;
1681
+ assert(iterator__has_been_accessed(i));
1552
1682
 
1553
- ps = git_vector_get(&fi->stack->entries, fi->stack->index);
1554
- if (!ps)
1555
- return GIT_ITEROVER;
1683
+ current_frame = filesystem_iterator_current_frame(iter);
1684
+ assert(current_frame);
1685
+ current_entry = filesystem_iterator_current_entry(current_frame);
1686
+ assert(current_entry);
1556
1687
 
1557
- git_buf_truncate(&fi->path, fi->root_len);
1558
- if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
1559
- return -1;
1688
+ if ((error = git_iterator_current(&entry, i)) < 0)
1689
+ return error;
1560
1690
 
1561
- if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
1562
- return GIT_ITEROVER;
1691
+ if (!S_ISDIR(entry->mode)) {
1692
+ if (filesystem_iterator_current_is_ignored(iter))
1693
+ *status = GIT_ITERATOR_STATUS_IGNORED;
1563
1694
 
1564
- fi->entry.path = ps->path;
1565
- fi->pathlist_match = ps->pathlist_match;
1566
- git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
1695
+ return filesystem_iterator_advance(out, i);
1696
+ }
1567
1697
 
1568
- /* need different mode here to keep directories during iteration */
1569
- fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
1698
+ git_buf_clear(&iter->tmp_buf);
1699
+ if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0)
1700
+ return error;
1570
1701
 
1571
- /* allow wrapper to check/update the entry (can force skip) */
1572
- if (fi->update_entry_cb &&
1573
- fi->update_entry_cb(fi) == GIT_ENOTFOUND) {
1574
- fs_iterator__advance_over_internal(&fi->base);
1575
- continue;
1576
- }
1702
+ base = iter->tmp_buf.ptr;
1703
+
1704
+ /* scan inside the directory looking for files. if we find nothing,
1705
+ * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
1706
+ * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
1707
+ * and then stop.
1708
+ *
1709
+ * however, if we're here looking for a pathlist item (but are not
1710
+ * actually in the pathlist ourselves) then start at FILTERED instead of
1711
+ * EMPTY. callers then know that this path was not something they asked
1712
+ * about.
1713
+ */
1714
+ *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ?
1715
+ GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY;
1716
+
1717
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
1718
+ if (filesystem_iterator_current_is_ignored(iter)) {
1719
+ /* if we found an explicitly ignored item, then update from
1720
+ * EMPTY to IGNORED
1721
+ */
1722
+ *status = GIT_ITERATOR_STATUS_IGNORED;
1723
+ } else if (S_ISDIR(entry->mode)) {
1724
+ error = filesystem_iterator_advance_into(&entry, i);
1577
1725
 
1578
- /* if this is a tree and trees aren't included, then skip */
1579
- if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
1580
- int error = fs_iterator__advance_into(NULL, &fi->base);
1726
+ if (!error)
1727
+ continue;
1581
1728
 
1582
- if (error != GIT_ENOTFOUND)
1583
- return error;
1729
+ /* this directory disappeared, ignore it */
1730
+ else if (error == GIT_ENOTFOUND)
1731
+ error = 0;
1584
1732
 
1585
- giterr_clear();
1586
- fs_iterator__advance_over_internal(&fi->base);
1587
- continue;
1733
+ /* a real error occurred */
1734
+ else
1735
+ break;
1736
+ } else {
1737
+ /* we found a non-ignored item, treat parent as untracked */
1738
+ *status = GIT_ITERATOR_STATUS_NORMAL;
1739
+ break;
1588
1740
  }
1589
1741
 
1590
- break;
1742
+ if ((error = git_iterator_advance(&entry, i)) < 0)
1743
+ break;
1591
1744
  }
1592
1745
 
1593
- return 0;
1594
- }
1595
-
1596
- static int fs_iterator__initialize(
1597
- git_iterator **out, fs_iterator *fi, const char *root)
1598
- {
1599
- int error;
1600
-
1601
- if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
1602
- git__free(fi);
1603
- return -1;
1746
+ /* wrap up scan back to base directory */
1747
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
1748
+ if ((error = git_iterator_advance(&entry, i)) < 0)
1749
+ break;
1604
1750
  }
1605
- fi->root_len = fi->path.size;
1606
- fi->pathlist_match = ITERATOR_PATHLIST_MATCH_CHILD;
1607
-
1608
- fi->dirload_flags =
1609
- (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1610
- (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
1611
- GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1612
1751
 
1613
- if ((error = fs_iterator__expand_dir(fi)) < 0) {
1614
- if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
1615
- giterr_clear();
1616
- error = 0;
1617
- } else {
1618
- git_iterator_free((git_iterator *)fi);
1619
- fi = NULL;
1620
- }
1621
- }
1752
+ if (!error)
1753
+ *out = entry;
1622
1754
 
1623
- *out = (git_iterator *)fi;
1624
1755
  return error;
1625
1756
  }
1626
1757
 
1627
- int git_iterator_for_filesystem(
1628
- git_iterator **out,
1629
- const char *root,
1630
- git_iterator_options *options)
1758
+ static void filesystem_iterator_clear(filesystem_iterator *iter)
1631
1759
  {
1632
- fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
1633
- GITERR_CHECK_ALLOC(fi);
1760
+ while (iter->frames.size)
1761
+ filesystem_iterator_frame_pop(iter);
1634
1762
 
1635
- ITERATOR_BASE_INIT(fi, fs, FS, NULL);
1763
+ git_array_clear(iter->frames);
1764
+ git_ignore__free(&iter->ignores);
1636
1765
 
1637
- if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0)
1638
- fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
1766
+ git_buf_free(&iter->tmp_buf);
1639
1767
 
1640
- return fs_iterator__initialize(out, fi, root);
1768
+ iterator_clear(&iter->base);
1641
1769
  }
1642
1770
 
1643
-
1644
- typedef struct {
1645
- fs_iterator fi;
1646
- git_ignores ignores;
1647
- int is_ignored;
1648
-
1649
- /*
1650
- * We may have a tree or the index+snapshot to compare against
1651
- * when checking for submodules.
1652
- */
1653
- git_tree *tree;
1654
- git_index *index;
1655
- git_vector index_snapshot;
1656
- git_vector_cmp entry_srch;
1657
-
1658
- } workdir_iterator;
1659
-
1660
- GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
1771
+ static int filesystem_iterator_init(filesystem_iterator *iter)
1661
1772
  {
1662
- size_t len;
1773
+ int error;
1663
1774
 
1664
- if (!path || (len = path->size) < 4)
1665
- return false;
1775
+ if (iterator__honor_ignores(&iter->base) &&
1776
+ (error = git_ignore__for_path(iter->base.repo,
1777
+ ".gitignore", &iter->ignores)) < 0)
1778
+ return error;
1666
1779
 
1667
- if (path->ptr[len - 1] == '/')
1668
- len--;
1780
+ if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0)
1781
+ return error;
1669
1782
 
1670
- if (git__tolower(path->ptr[len - 1]) != 't' ||
1671
- git__tolower(path->ptr[len - 2]) != 'i' ||
1672
- git__tolower(path->ptr[len - 3]) != 'g' ||
1673
- git__tolower(path->ptr[len - 4]) != '.')
1674
- return false;
1783
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
1675
1784
 
1676
- return (len == 4 || path->ptr[len - 5] == '/');
1785
+ return 0;
1677
1786
  }
1678
1787
 
1679
- /**
1680
- * Figure out if an entry is a submodule.
1681
- *
1682
- * We consider it a submodule if the path is listed as a submodule in
1683
- * either the tree or the index.
1684
- */
1685
- static int is_submodule(workdir_iterator *wi, fs_iterator_path_with_stat *ie)
1788
+ static int filesystem_iterator_reset(git_iterator *i)
1686
1789
  {
1687
- int error, is_submodule = 0;
1688
-
1689
- if (wi->tree) {
1690
- git_tree_entry *e;
1790
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1691
1791
 
1692
- /* remove the trailing slash for finding */
1693
- ie->path[ie->path_len-1] = '\0';
1694
- error = git_tree_entry_bypath(&e, wi->tree, ie->path);
1695
- ie->path[ie->path_len-1] = '/';
1696
- if (error < 0 && error != GIT_ENOTFOUND)
1697
- return 0;
1698
- if (!error) {
1699
- is_submodule = e->attr == GIT_FILEMODE_COMMIT;
1700
- git_tree_entry_free(e);
1701
- }
1702
- }
1703
-
1704
- if (!is_submodule && wi->index) {
1705
- git_index_entry *e;
1706
- size_t pos;
1707
-
1708
- error = git_index_snapshot_find(&pos, &wi->index_snapshot, wi->entry_srch, ie->path, ie->path_len-1, 0);
1709
- if (error < 0 && error != GIT_ENOTFOUND)
1710
- return 0;
1711
-
1712
- if (!error) {
1713
- e = git_vector_get(&wi->index_snapshot, pos);
1714
-
1715
- is_submodule = e->mode == GIT_FILEMODE_COMMIT;
1716
- }
1717
- }
1718
-
1719
- return is_submodule;
1792
+ filesystem_iterator_clear(iter);
1793
+ return filesystem_iterator_init(iter);
1720
1794
  }
1721
1795
 
1722
- GIT_INLINE(git_dir_flag) git_entry__dir_flag(git_index_entry *entry) {
1723
- #if defined(GIT_WIN32) && !defined(__MINGW32__)
1724
- return (entry && entry->mode)
1725
- ? S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE
1726
- : GIT_DIR_FLAG_UNKNOWN;
1727
- #else
1728
- GIT_UNUSED(entry);
1729
- return GIT_DIR_FLAG_UNKNOWN;
1730
- #endif
1796
+ static void filesystem_iterator_free(git_iterator *i)
1797
+ {
1798
+ filesystem_iterator *iter = (filesystem_iterator *)i;
1799
+ git__free(iter->root);
1800
+ git_buf_free(&iter->current_path);
1801
+ git_tree_free(iter->tree);
1802
+ if (iter->index)
1803
+ git_index_snapshot_release(&iter->index_snapshot, iter->index);
1804
+ filesystem_iterator_clear(iter);
1731
1805
  }
1732
1806
 
1733
- static int workdir_iterator__enter_dir(fs_iterator *fi)
1807
+ static int iterator_for_filesystem(
1808
+ git_iterator **out,
1809
+ git_repository *repo,
1810
+ const char *root,
1811
+ git_index *index,
1812
+ git_tree *tree,
1813
+ git_iterator_type_t type,
1814
+ git_iterator_options *options)
1734
1815
  {
1735
- workdir_iterator *wi = (workdir_iterator *)fi;
1736
- fs_iterator_frame *ff = fi->stack;
1737
- size_t pos;
1738
- fs_iterator_path_with_stat *entry;
1739
- bool found_submodules = false;
1816
+ filesystem_iterator *iter;
1817
+ size_t root_len;
1818
+ int error;
1740
1819
 
1741
- git_dir_flag dir_flag = git_entry__dir_flag(&fi->entry);
1820
+ static git_iterator_callbacks callbacks = {
1821
+ filesystem_iterator_current,
1822
+ filesystem_iterator_advance,
1823
+ filesystem_iterator_advance_into,
1824
+ filesystem_iterator_advance_over,
1825
+ filesystem_iterator_reset,
1826
+ filesystem_iterator_free
1827
+ };
1742
1828
 
1743
- /* check if this directory is ignored */
1744
- if (git_ignore__lookup(&ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len, dir_flag) < 0) {
1745
- giterr_clear();
1746
- ff->is_ignored = GIT_IGNORE_NOTFOUND;
1747
- }
1829
+ *out = NULL;
1748
1830
 
1749
- /* if this is not the top level directory... */
1750
- if (ff->next != NULL) {
1751
- ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
1831
+ if (root == NULL)
1832
+ return git_iterator_for_nothing(out, options);
1752
1833
 
1753
- /* inherit ignored from parent if no rule specified */
1754
- if (ff->is_ignored <= GIT_IGNORE_NOTFOUND)
1755
- ff->is_ignored = ff->next->is_ignored;
1834
+ iter = git__calloc(1, sizeof(filesystem_iterator));
1835
+ GITERR_CHECK_ALLOC(iter);
1756
1836
 
1757
- /* push new ignores for files in this directory */
1758
- (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
1759
- }
1837
+ root_len = strlen(root);
1760
1838
 
1761
- /* convert submodules to GITLINK and remove trailing slashes */
1762
- git_vector_foreach(&ff->entries, pos, entry) {
1763
- if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
1764
- continue;
1839
+ iter->root = git__malloc(root_len+2);
1840
+ GITERR_CHECK_ALLOC(iter->root);
1765
1841
 
1766
- if (is_submodule(wi, entry)) {
1767
- entry->st.st_mode = GIT_FILEMODE_COMMIT;
1768
- entry->path_len--;
1769
- entry->path[entry->path_len] = '\0';
1770
- found_submodules = true;
1771
- }
1772
- }
1842
+ memcpy(iter->root, root, root_len);
1773
1843
 
1774
- /* if we renamed submodules, re-sort and re-seek to start */
1775
- if (found_submodules) {
1776
- git_vector_set_sorted(&ff->entries, 0);
1777
- git_vector_sort(&ff->entries);
1778
- fs_iterator__seek_frame_start(fi, ff);
1844
+ if (root_len == 0 || root[root_len-1] != '/') {
1845
+ iter->root[root_len] = '/';
1846
+ root_len++;
1779
1847
  }
1848
+ iter->root[root_len] = '\0';
1849
+ iter->root_len = root_len;
1780
1850
 
1781
- return 0;
1782
- }
1851
+ if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0)
1852
+ goto on_error;
1783
1853
 
1784
- static int workdir_iterator__leave_dir(fs_iterator *fi)
1785
- {
1786
- workdir_iterator *wi = (workdir_iterator *)fi;
1787
- git_ignore__pop_dir(&wi->ignores);
1788
- return 0;
1789
- }
1854
+ iter->base.type = type;
1855
+ iter->base.cb = &callbacks;
1790
1856
 
1791
- static int workdir_iterator__update_entry(fs_iterator *fi)
1792
- {
1793
- workdir_iterator *wi = (workdir_iterator *)fi;
1857
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0)
1858
+ goto on_error;
1859
+
1860
+ if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0)
1861
+ goto on_error;
1862
+
1863
+ if (index &&
1864
+ (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0)
1865
+ goto on_error;
1794
1866
 
1795
- /* skip over .git entries */
1796
- if (workdir_path_is_dotgit(&fi->path))
1797
- return GIT_ENOTFOUND;
1867
+ iter->index = index;
1868
+ iter->dirload_flags =
1869
+ (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
1870
+ (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
1871
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
1798
1872
 
1799
- /* reset is_ignored since we haven't checked yet */
1800
- wi->is_ignored = GIT_IGNORE_UNCHECKED;
1873
+ if ((error = filesystem_iterator_init(iter)) < 0)
1874
+ goto on_error;
1801
1875
 
1876
+ *out = &iter->base;
1802
1877
  return 0;
1878
+
1879
+ on_error:
1880
+ git__free(iter->root);
1881
+ git_buf_free(&iter->current_path);
1882
+ git_iterator_free(&iter->base);
1883
+ return error;
1803
1884
  }
1804
1885
 
1805
- static void workdir_iterator__free(git_iterator *self)
1886
+ int git_iterator_for_filesystem(
1887
+ git_iterator **out,
1888
+ const char *root,
1889
+ git_iterator_options *options)
1806
1890
  {
1807
- workdir_iterator *wi = (workdir_iterator *)self;
1808
- if (wi->index)
1809
- git_index_snapshot_release(&wi->index_snapshot, wi->index);
1810
- git_tree_free(wi->tree);
1811
- fs_iterator__free(self);
1812
- git_ignore__free(&wi->ignores);
1891
+ return iterator_for_filesystem(out,
1892
+ NULL, root, NULL, NULL, GIT_ITERATOR_TYPE_FS, options);
1813
1893
  }
1814
1894
 
1815
1895
  int git_iterator_for_workdir_ext(
@@ -1818,299 +1898,326 @@ int git_iterator_for_workdir_ext(
1818
1898
  const char *repo_workdir,
1819
1899
  git_index *index,
1820
1900
  git_tree *tree,
1821
- git_iterator_options *options)
1901
+ git_iterator_options *given_opts)
1822
1902
  {
1823
- int error, precompose = 0;
1824
- workdir_iterator *wi;
1903
+ git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
1825
1904
 
1826
1905
  if (!repo_workdir) {
1827
1906
  if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1828
1907
  return GIT_EBAREREPO;
1829
- repo_workdir = git_repository_workdir(repo);
1830
- }
1831
1908
 
1832
- /* initialize as an fs iterator then do overrides */
1833
- wi = git__calloc(1, sizeof(workdir_iterator));
1834
- GITERR_CHECK_ALLOC(wi);
1835
- ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
1836
-
1837
- wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
1838
- wi->fi.cb.free = workdir_iterator__free;
1839
- wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
1840
- wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
1841
- wi->fi.update_entry_cb = workdir_iterator__update_entry;
1842
-
1843
- if ((error = iterator__update_ignore_case((git_iterator *)wi, options ? options->flags : 0)) < 0 ||
1844
- (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
1845
- {
1846
- git_iterator_free((git_iterator *)wi);
1847
- return error;
1909
+ repo_workdir = git_repository_workdir(repo);
1848
1910
  }
1849
1911
 
1850
- if (tree && (error = git_object_dup((git_object **)&wi->tree, (git_object *)tree)) < 0)
1851
- return error;
1852
-
1853
- wi->index = index;
1854
- if (index && (error = git_index_snapshot_new(&wi->index_snapshot, index)) < 0) {
1855
- git_iterator_free((git_iterator *)wi);
1856
- return error;
1857
- }
1858
- wi->entry_srch = iterator__ignore_case(wi) ?
1859
- git_index_entry_isrch : git_index_entry_srch;
1912
+ /* upgrade to a workdir iterator, adding necessary internal flags */
1913
+ if (given_opts)
1914
+ memcpy(&options, given_opts, sizeof(git_iterator_options));
1860
1915
 
1916
+ options.flags |= GIT_ITERATOR_HONOR_IGNORES |
1917
+ GIT_ITERATOR_IGNORE_DOT_GIT;
1861
1918
 
1862
- /* try to look up precompose and set flag if appropriate */
1863
- if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
1864
- giterr_clear();
1865
- else if (precompose)
1866
- wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
1867
-
1868
- return fs_iterator__initialize(out, &wi->fi, repo_workdir);
1919
+ return iterator_for_filesystem(out,
1920
+ repo, repo_workdir, index, tree, GIT_ITERATOR_TYPE_WORKDIR, &options);
1869
1921
  }
1870
1922
 
1871
- void git_iterator_free(git_iterator *iter)
1872
- {
1873
- if (iter == NULL)
1874
- return;
1875
1923
 
1876
- iter->cb->free(iter);
1924
+ /* Index iterator */
1877
1925
 
1878
- git_vector_free(&iter->pathlist);
1879
- git__free(iter->start);
1880
- git__free(iter->end);
1881
1926
 
1882
- memset(iter, 0, sizeof(*iter));
1927
+ typedef struct {
1928
+ git_iterator base;
1929
+ git_vector entries;
1930
+ size_t next_idx;
1883
1931
 
1884
- git__free(iter);
1885
- }
1932
+ /* the pseudotree entry */
1933
+ git_index_entry tree_entry;
1934
+ git_buf tree_buf;
1935
+ bool skip_tree;
1936
+
1937
+ const git_index_entry *entry;
1938
+ } index_iterator;
1886
1939
 
1887
- int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
1940
+ static int index_iterator_current(
1941
+ const git_index_entry **out, git_iterator *i)
1888
1942
  {
1889
- bool desire_ignore_case = (ignore_case != 0);
1943
+ index_iterator *iter = (index_iterator *)i;
1890
1944
 
1891
- if (iterator__ignore_case(iter) == desire_ignore_case)
1892
- return 0;
1945
+ if (!iterator__has_been_accessed(i))
1946
+ return iter->base.cb->advance(out, i);
1893
1947
 
1894
- if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
1895
- if (desire_ignore_case)
1896
- iter->flags |= GIT_ITERATOR_IGNORE_CASE;
1897
- else
1898
- iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
1899
- } else {
1900
- giterr_set(GITERR_INVALID,
1901
- "Cannot currently set ignore case on non-empty iterators");
1902
- return -1;
1948
+ if (iter->entry == NULL) {
1949
+ *out = NULL;
1950
+ return GIT_ITEROVER;
1903
1951
  }
1904
1952
 
1953
+ *out = iter->entry;
1905
1954
  return 0;
1906
1955
  }
1907
1956
 
1908
- git_index *git_iterator_get_index(git_iterator *iter)
1957
+ static bool index_iterator_create_pseudotree(
1958
+ const git_index_entry **out,
1959
+ index_iterator *iter,
1960
+ const char *path)
1909
1961
  {
1910
- if (iter->type == GIT_ITERATOR_TYPE_INDEX)
1911
- return ((index_iterator *)iter)->index;
1912
- return NULL;
1913
- }
1962
+ const char *prev_path, *relative_path, *dirsep;
1963
+ size_t common_len;
1914
1964
 
1915
- int git_iterator_current_tree_entry(
1916
- const git_tree_entry **tree_entry, git_iterator *iter)
1917
- {
1918
- if (iter->type != GIT_ITERATOR_TYPE_TREE)
1919
- *tree_entry = NULL;
1920
- else {
1921
- tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
1922
- *tree_entry = (tf->current < tf->n_entries) ?
1923
- tf->entries[tf->current]->te : NULL;
1924
- }
1965
+ prev_path = iter->entry ? iter->entry->path : "";
1925
1966
 
1926
- return 0;
1967
+ /* determine if the new path is in a different directory from the old */
1968
+ common_len = git_path_common_dirlen(prev_path, path);
1969
+ relative_path = path + common_len;
1970
+
1971
+ if ((dirsep = strchr(relative_path, '/')) == NULL)
1972
+ return false;
1973
+
1974
+ git_buf_clear(&iter->tree_buf);
1975
+ git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1);
1976
+
1977
+ iter->tree_entry.mode = GIT_FILEMODE_TREE;
1978
+ iter->tree_entry.path = iter->tree_buf.ptr;
1979
+
1980
+ *out = &iter->tree_entry;
1981
+ return true;
1927
1982
  }
1928
1983
 
1929
- int git_iterator_current_parent_tree(
1930
- const git_tree **tree_ptr,
1931
- git_iterator *iter,
1932
- const char *parent_path)
1984
+ static int index_iterator_skip_pseudotree(index_iterator *iter)
1933
1985
  {
1934
- tree_iterator *ti = (tree_iterator *)iter;
1935
- tree_iterator_frame *tf;
1936
- const char *scan = parent_path;
1937
- const git_tree_entry *te;
1986
+ assert(iterator__has_been_accessed(&iter->base));
1987
+ assert(S_ISDIR(iter->entry->mode));
1938
1988
 
1939
- *tree_ptr = NULL;
1989
+ while (true) {
1990
+ const git_index_entry *next_entry = NULL;
1940
1991
 
1941
- if (iter->type != GIT_ITERATOR_TYPE_TREE)
1942
- return 0;
1992
+ if (++iter->next_idx >= iter->entries.length)
1993
+ return GIT_ITEROVER;
1943
1994
 
1944
- for (tf = ti->root; *scan; ) {
1945
- if (!(tf = tf->down) ||
1946
- tf->current >= tf->n_entries ||
1947
- !(te = tf->entries[tf->current]->te) ||
1948
- ti->base.strncomp(scan, te->filename, te->filename_len) != 0)
1949
- return 0;
1995
+ next_entry = iter->entries.contents[iter->next_idx];
1950
1996
 
1951
- scan += te->filename_len;
1952
- if (*scan == '/')
1953
- scan++;
1997
+ if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path,
1998
+ iter->tree_buf.size) != 0)
1999
+ break;
1954
2000
  }
1955
2001
 
1956
- *tree_ptr = tf->entries[tf->current]->tree;
2002
+ iter->skip_tree = false;
1957
2003
  return 0;
1958
2004
  }
1959
2005
 
1960
- static void workdir_iterator_update_is_ignored(workdir_iterator *wi)
2006
+ static int index_iterator_advance(
2007
+ const git_index_entry **out, git_iterator *i)
1961
2008
  {
1962
- git_dir_flag dir_flag = git_entry__dir_flag(&wi->fi.entry);
2009
+ index_iterator *iter = (index_iterator *)i;
2010
+ const git_index_entry *entry = NULL;
2011
+ bool is_submodule;
2012
+ int error = 0;
1963
2013
 
1964
- if (git_ignore__lookup(&wi->is_ignored, &wi->ignores, wi->fi.entry.path, dir_flag) < 0) {
1965
- giterr_clear();
1966
- wi->is_ignored = GIT_IGNORE_NOTFOUND;
1967
- }
2014
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
1968
2015
 
1969
- /* use ignore from containing frame stack */
1970
- if (wi->is_ignored <= GIT_IGNORE_NOTFOUND)
1971
- wi->is_ignored = wi->fi.stack->is_ignored;
1972
- }
2016
+ while (true) {
2017
+ if (iter->next_idx >= iter->entries.length) {
2018
+ error = GIT_ITEROVER;
2019
+ break;
2020
+ }
1973
2021
 
1974
- bool git_iterator_current_is_ignored(git_iterator *iter)
1975
- {
1976
- workdir_iterator *wi = (workdir_iterator *)iter;
2022
+ /* we were not asked to expand this pseudotree. advance over it. */
2023
+ if (iter->skip_tree) {
2024
+ index_iterator_skip_pseudotree(iter);
2025
+ continue;
2026
+ }
1977
2027
 
1978
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1979
- return false;
2028
+ entry = iter->entries.contents[iter->next_idx];
2029
+ is_submodule = S_ISGITLINK(entry->mode);
1980
2030
 
1981
- if (wi->is_ignored != GIT_IGNORE_UNCHECKED)
1982
- return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
2031
+ if (!iterator_has_started(&iter->base, entry->path, is_submodule)) {
2032
+ iter->next_idx++;
2033
+ continue;
2034
+ }
1983
2035
 
1984
- workdir_iterator_update_is_ignored(wi);
2036
+ if (iterator_has_ended(&iter->base, entry->path)) {
2037
+ error = GIT_ITEROVER;
2038
+ break;
2039
+ }
2040
+
2041
+ /* if we have a list of paths we're interested in, examine it */
2042
+ if (!iterator_pathlist_next_is(&iter->base, entry->path)) {
2043
+ iter->next_idx++;
2044
+ continue;
2045
+ }
2046
+
2047
+ /* if this is a conflict, skip it unless we're including conflicts */
2048
+ if (git_index_entry_is_conflict(entry) &&
2049
+ !iterator__include_conflicts(&iter->base)) {
2050
+ iter->next_idx++;
2051
+ continue;
2052
+ }
2053
+
2054
+ /* we've found what will be our next _file_ entry. but if we are
2055
+ * returning trees entries, we may need to return a pseudotree
2056
+ * entry that will contain this. don't advance over this entry,
2057
+ * though, we still need to return it on the next `advance`.
2058
+ */
2059
+ if (iterator__include_trees(&iter->base) &&
2060
+ index_iterator_create_pseudotree(&entry, iter, entry->path)) {
2061
+
2062
+ /* Note whether this pseudo tree should be expanded or not */
2063
+ iter->skip_tree = iterator__dont_autoexpand(&iter->base);
2064
+ break;
2065
+ }
1985
2066
 
1986
- return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
2067
+ iter->next_idx++;
2068
+ break;
2069
+ }
2070
+
2071
+ iter->entry = (error == 0) ? entry : NULL;
2072
+
2073
+ if (out)
2074
+ *out = iter->entry;
2075
+
2076
+ return error;
1987
2077
  }
1988
2078
 
1989
- bool git_iterator_current_tree_is_ignored(git_iterator *iter)
2079
+ static int index_iterator_advance_into(
2080
+ const git_index_entry **out, git_iterator *i)
1990
2081
  {
1991
- workdir_iterator *wi = (workdir_iterator *)iter;
2082
+ index_iterator *iter = (index_iterator *)i;
1992
2083
 
1993
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
1994
- return false;
2084
+ if (! S_ISDIR(iter->tree_entry.mode)) {
2085
+ if (out)
2086
+ *out = NULL;
2087
+
2088
+ return 0;
2089
+ }
1995
2090
 
1996
- return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
2091
+ iter->skip_tree = false;
2092
+ return index_iterator_advance(out, i);
1997
2093
  }
1998
2094
 
1999
- int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
2095
+ static int index_iterator_advance_over(
2096
+ const git_index_entry **out,
2097
+ git_iterator_status_t *status,
2098
+ git_iterator *i)
2000
2099
  {
2100
+ index_iterator *iter = (index_iterator *)i;
2001
2101
  const git_index_entry *entry;
2102
+ int error;
2002
2103
 
2003
- /* a "done" iterator is after every prefix */
2004
- if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
2005
- return 1;
2104
+ if ((error = index_iterator_current(&entry, i)) < 0)
2105
+ return error;
2006
2106
 
2007
- /* a NULL prefix is after any valid iterator */
2008
- if (!path_prefix)
2009
- return -1;
2107
+ if (S_ISDIR(entry->mode))
2108
+ index_iterator_skip_pseudotree(iter);
2010
2109
 
2011
- return iter->prefixcomp(entry->path, path_prefix);
2110
+ *status = GIT_ITERATOR_STATUS_NORMAL;
2111
+ return index_iterator_advance(out, i);
2012
2112
  }
2013
2113
 
2014
- int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
2114
+ static void index_iterator_clear(index_iterator *iter)
2015
2115
  {
2016
- workdir_iterator *wi = (workdir_iterator *)iter;
2017
-
2018
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
2019
- *path = NULL;
2020
- else
2021
- *path = &wi->fi.path;
2116
+ iterator_clear(&iter->base);
2117
+ }
2022
2118
 
2119
+ static int index_iterator_init(index_iterator *iter)
2120
+ {
2121
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
2122
+ iter->next_idx = 0;
2123
+ iter->skip_tree = false;
2023
2124
  return 0;
2024
2125
  }
2025
2126
 
2026
- int git_iterator_index(git_index **out, git_iterator *iter)
2127
+ static int index_iterator_reset(git_iterator *i)
2027
2128
  {
2028
- workdir_iterator *wi = (workdir_iterator *)iter;
2129
+ index_iterator *iter = (index_iterator *)i;
2029
2130
 
2030
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
2031
- *out = NULL;
2131
+ index_iterator_clear(iter);
2132
+ return index_iterator_init(iter);
2133
+ }
2032
2134
 
2033
- *out = wi->index;
2135
+ static void index_iterator_free(git_iterator *i)
2136
+ {
2137
+ index_iterator *iter = (index_iterator *)i;
2034
2138
 
2035
- return 0;
2139
+ git_index_snapshot_release(&iter->entries, iter->base.index);
2140
+ git_buf_free(&iter->tree_buf);
2036
2141
  }
2037
2142
 
2038
- int git_iterator_advance_over_with_status(
2039
- const git_index_entry **entryptr,
2040
- git_iterator_status_t *status,
2041
- git_iterator *iter)
2143
+ int git_iterator_for_index(
2144
+ git_iterator **out,
2145
+ git_repository *repo,
2146
+ git_index *index,
2147
+ git_iterator_options *options)
2042
2148
  {
2043
- int error = 0;
2044
- workdir_iterator *wi = (workdir_iterator *)iter;
2045
- char *base = NULL;
2046
- const git_index_entry *entry;
2149
+ index_iterator *iter;
2150
+ int error;
2047
2151
 
2048
- *status = GIT_ITERATOR_STATUS_NORMAL;
2152
+ static git_iterator_callbacks callbacks = {
2153
+ index_iterator_current,
2154
+ index_iterator_advance,
2155
+ index_iterator_advance_into,
2156
+ index_iterator_advance_over,
2157
+ index_iterator_reset,
2158
+ index_iterator_free
2159
+ };
2049
2160
 
2050
- if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
2051
- return git_iterator_advance(entryptr, iter);
2052
- if ((error = git_iterator_current(&entry, iter)) < 0)
2053
- return error;
2161
+ *out = NULL;
2054
2162
 
2055
- if (!S_ISDIR(entry->mode)) {
2056
- workdir_iterator_update_is_ignored(wi);
2057
- if (wi->is_ignored == GIT_IGNORE_TRUE)
2058
- *status = GIT_ITERATOR_STATUS_IGNORED;
2059
- return git_iterator_advance(entryptr, iter);
2060
- }
2163
+ if (index == NULL)
2164
+ return git_iterator_for_nothing(out, options);
2061
2165
 
2062
- *status = GIT_ITERATOR_STATUS_EMPTY;
2166
+ iter = git__calloc(1, sizeof(index_iterator));
2167
+ GITERR_CHECK_ALLOC(iter);
2063
2168
 
2064
- base = git__strdup(entry->path);
2065
- GITERR_CHECK_ALLOC(base);
2169
+ iter->base.type = GIT_ITERATOR_TYPE_INDEX;
2170
+ iter->base.cb = &callbacks;
2066
2171
 
2067
- /* scan inside directory looking for a non-ignored item */
2068
- while (entry && !iter->prefixcomp(entry->path, base)) {
2069
- workdir_iterator_update_is_ignored(wi);
2172
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 ||
2173
+ (error = git_index_snapshot_new(&iter->entries, index)) < 0 ||
2174
+ (error = index_iterator_init(iter)) < 0)
2175
+ goto on_error;
2070
2176
 
2071
- /* if we found an explicitly ignored item, then update from
2072
- * EMPTY to IGNORED
2073
- */
2074
- if (wi->is_ignored == GIT_IGNORE_TRUE)
2075
- *status = GIT_ITERATOR_STATUS_IGNORED;
2076
- else if (S_ISDIR(entry->mode)) {
2077
- error = git_iterator_advance_into(&entry, iter);
2177
+ git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ?
2178
+ git_index_entry_icmp : git_index_entry_cmp);
2179
+ git_vector_sort(&iter->entries);
2078
2180
 
2079
- if (!error)
2080
- continue;
2181
+ *out = &iter->base;
2182
+ return 0;
2081
2183
 
2082
- else if (error == GIT_ENOTFOUND) {
2083
- /* we entered this directory only hoping to find child matches to
2084
- * our pathlist (eg, this is `foo` and we had a pathlist entry for
2085
- * `foo/bar`). it should not be ignored, it should be excluded.
2086
- */
2087
- if (wi->fi.pathlist_match == ITERATOR_PATHLIST_MATCH_CHILD)
2088
- *status = GIT_ITERATOR_STATUS_FILTERED;
2089
- else
2090
- wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
2184
+ on_error:
2185
+ git_iterator_free(&iter->base);
2186
+ return error;
2187
+ }
2091
2188
 
2092
- error = 0;
2093
- } else
2094
- break; /* real error, stop here */
2095
- } else {
2096
- /* we found a non-ignored item, treat parent as untracked */
2097
- *status = GIT_ITERATOR_STATUS_NORMAL;
2098
- break;
2099
- }
2100
2189
 
2101
- if ((error = git_iterator_advance(&entry, iter)) < 0)
2102
- break;
2103
- }
2190
+ /* Iterator API */
2104
2191
 
2105
- /* wrap up scan back to base directory */
2106
- while (entry && !iter->prefixcomp(entry->path, base))
2107
- if ((error = git_iterator_advance(&entry, iter)) < 0)
2108
- break;
2192
+ int git_iterator_reset_range(
2193
+ git_iterator *i, const char *start, const char *end)
2194
+ {
2195
+ if (iterator_reset_range(i, start, end) < 0)
2196
+ return -1;
2109
2197
 
2110
- *entryptr = entry;
2111
- git__free(base);
2198
+ return i->cb->reset(i);
2199
+ }
2112
2200
 
2113
- return error;
2201
+ void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
2202
+ {
2203
+ assert(!iterator__has_been_accessed(i));
2204
+ iterator_set_ignore_case(i, ignore_case);
2205
+ }
2206
+
2207
+ void git_iterator_free(git_iterator *iter)
2208
+ {
2209
+ if (iter == NULL)
2210
+ return;
2211
+
2212
+ iter->cb->free(iter);
2213
+
2214
+ git_vector_free(&iter->pathlist);
2215
+ git__free(iter->start);
2216
+ git__free(iter->end);
2217
+
2218
+ memset(iter, 0, sizeof(*iter));
2219
+
2220
+ git__free(iter);
2114
2221
  }
2115
2222
 
2116
2223
  int git_iterator_walk(