2 * Copyright (C) 2009-2012 the libgit2 contributors
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
13 typedef struct tree_iterator_frame tree_iterator_frame
;
14 struct tree_iterator_frame
{
15 tree_iterator_frame
*next
;
23 tree_iterator_frame
*stack
;
24 git_index_entry entry
;
28 static const git_tree_entry
*tree_iterator__tree_entry(tree_iterator
*ti
)
30 return (ti
->stack
== NULL
) ? NULL
:
31 git_tree_entry_byindex(ti
->stack
->tree
, ti
->stack
->index
);
34 static int tree_iterator__current(
35 git_iterator
*self
, const git_index_entry
**entry
)
38 tree_iterator
*ti
= (tree_iterator
*)self
;
39 const git_tree_entry
*te
= tree_iterator__tree_entry(ti
);
46 ti
->entry
.mode
= te
->attr
;
47 git_oid_cpy(&ti
->entry
.oid
, &te
->oid
);
48 error
= git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
);
49 if (error
< GIT_SUCCESS
)
51 ti
->entry
.path
= ti
->path
.ptr
;
58 static int tree_iterator__at_end(git_iterator
*self
)
60 return (tree_iterator__tree_entry((tree_iterator
*)self
) == NULL
);
63 static tree_iterator_frame
*tree_iterator__alloc_frame(git_tree
*tree
)
65 tree_iterator_frame
*tf
= git__calloc(1, sizeof(tree_iterator_frame
));
70 static int tree_iterator__expand_tree(tree_iterator
*ti
)
74 const git_tree_entry
*te
= tree_iterator__tree_entry(ti
);
75 tree_iterator_frame
*tf
;
77 while (te
!= NULL
&& entry_is_tree(te
)) {
78 error
= git_tree_lookup(&subtree
, ti
->repo
, &te
->oid
);
79 if (error
!= GIT_SUCCESS
)
82 if ((tf
= tree_iterator__alloc_frame(subtree
)) == NULL
)
88 error
= git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
);
89 if (error
< GIT_SUCCESS
)
92 te
= tree_iterator__tree_entry(ti
);
98 static void tree_iterator__pop_frame(tree_iterator
*ti
)
100 tree_iterator_frame
*tf
= ti
->stack
;
101 ti
->stack
= tf
->next
;
102 if (ti
->stack
!= NULL
) /* don't free the initial tree */
103 git_tree_free(tf
->tree
);
107 static int tree_iterator__advance(
108 git_iterator
*self
, const git_index_entry
**entry
)
110 int error
= GIT_SUCCESS
;
111 tree_iterator
*ti
= (tree_iterator
*)self
;
112 const git_tree_entry
*te
= NULL
;
117 while (ti
->stack
!= NULL
) {
118 /* remove old entry filename */
119 git_buf_rtruncate_at_char(&ti
->path
, '/');
121 te
= git_tree_entry_byindex(ti
->stack
->tree
, ++ti
->stack
->index
);
125 tree_iterator__pop_frame(ti
);
126 git_buf_rtruncate_at_char(&ti
->path
, '/');
129 if (te
&& entry_is_tree(te
))
130 error
= tree_iterator__expand_tree(ti
);
132 if (error
== GIT_SUCCESS
&& entry
!= NULL
)
133 error
= tree_iterator__current(self
, entry
);
138 static void tree_iterator__free(git_iterator
*self
)
140 tree_iterator
*ti
= (tree_iterator
*)self
;
141 while (ti
->stack
!= NULL
)
142 tree_iterator__pop_frame(ti
);
143 git_buf_free(&ti
->path
);
146 static int tree_iterator__reset(git_iterator
*self
)
148 tree_iterator
*ti
= (tree_iterator
*)self
;
149 while (ti
->stack
&& ti
->stack
->next
)
150 tree_iterator__pop_frame(ti
);
152 ti
->stack
->index
= 0;
153 return tree_iterator__expand_tree(ti
);
156 int git_iterator_for_tree(
157 git_repository
*repo
, git_tree
*tree
, git_iterator
**iter
)
160 tree_iterator
*ti
= git__calloc(1, sizeof(tree_iterator
));
164 ti
->base
.type
= GIT_ITERATOR_TREE
;
165 ti
->base
.current
= tree_iterator__current
;
166 ti
->base
.at_end
= tree_iterator__at_end
;
167 ti
->base
.advance
= tree_iterator__advance
;
168 ti
->base
.reset
= tree_iterator__reset
;
169 ti
->base
.free
= tree_iterator__free
;
171 ti
->stack
= tree_iterator__alloc_frame(tree
);
173 if ((error
= tree_iterator__expand_tree(ti
)) < GIT_SUCCESS
)
174 git_iterator_free((git_iterator
*)ti
);
176 *iter
= (git_iterator
*)ti
;
185 unsigned int current
;
188 static int index_iterator__current(
189 git_iterator
*self
, const git_index_entry
**entry
)
191 index_iterator
*ii
= (index_iterator
*)self
;
192 *entry
= git_index_get(ii
->index
, ii
->current
);
196 static int index_iterator__at_end(git_iterator
*self
)
198 index_iterator
*ii
= (index_iterator
*)self
;
199 return (ii
->current
>= git_index_entrycount(ii
->index
));
202 static int index_iterator__advance(
203 git_iterator
*self
, const git_index_entry
**entry
)
205 index_iterator
*ii
= (index_iterator
*)self
;
206 if (ii
->current
< git_index_entrycount(ii
->index
))
209 *entry
= git_index_get(ii
->index
, ii
->current
);
213 static int index_iterator__reset(git_iterator
*self
)
215 index_iterator
*ii
= (index_iterator
*)self
;
220 static void index_iterator__free(git_iterator
*self
)
222 index_iterator
*ii
= (index_iterator
*)self
;
223 git_index_free(ii
->index
);
227 int git_iterator_for_index(git_repository
*repo
, git_iterator
**iter
)
230 index_iterator
*ii
= git__calloc(1, sizeof(index_iterator
));
234 ii
->base
.type
= GIT_ITERATOR_INDEX
;
235 ii
->base
.current
= index_iterator__current
;
236 ii
->base
.at_end
= index_iterator__at_end
;
237 ii
->base
.advance
= index_iterator__advance
;
238 ii
->base
.reset
= index_iterator__reset
;
239 ii
->base
.free
= index_iterator__free
;
242 if ((error
= git_repository_index(&ii
->index
, repo
)) < GIT_SUCCESS
)
245 *iter
= (git_iterator
*)ii
;
250 typedef struct workdir_iterator_frame workdir_iterator_frame
;
251 struct workdir_iterator_frame
{
252 workdir_iterator_frame
*next
;
259 git_repository
*repo
;
261 workdir_iterator_frame
*stack
;
263 git_index_entry entry
;
268 static workdir_iterator_frame
*workdir_iterator__alloc_frame(void)
270 workdir_iterator_frame
*wf
= git__calloc(1, sizeof(workdir_iterator_frame
));
273 if (git_vector_init(&wf
->entries
, 0, git_path_with_stat_cmp
) != GIT_SUCCESS
) {
280 static void workdir_iterator__free_frame(workdir_iterator_frame
*wf
)
283 git_path_with_stat
*path
;
285 git_vector_foreach(&wf
->entries
, i
, path
)
287 git_vector_free(&wf
->entries
);
291 static int workdir_iterator__update_entry(workdir_iterator
*wi
);
293 static int workdir_iterator__expand_dir(workdir_iterator
*wi
)
296 workdir_iterator_frame
*wf
= workdir_iterator__alloc_frame();
300 error
= git_path_dirload_with_stat(wi
->path
.ptr
, wi
->root_len
, &wf
->entries
);
301 if (error
< GIT_SUCCESS
|| wf
->entries
.length
== 0) {
302 workdir_iterator__free_frame(wf
);
303 return GIT_ENOTFOUND
;
306 git_vector_sort(&wf
->entries
);
307 wf
->next
= wi
->stack
;
310 /* only push new ignores if this is not top level directory */
311 if (wi
->stack
->next
!= NULL
) {
312 int slash_pos
= git_buf_rfind_next(&wi
->path
, '/');
313 (void)git_ignore__push_dir(&wi
->ignores
, &wi
->path
.ptr
[slash_pos
+ 1]);
316 return workdir_iterator__update_entry(wi
);
319 static int workdir_iterator__current(
320 git_iterator
*self
, const git_index_entry
**entry
)
322 workdir_iterator
*wi
= (workdir_iterator
*)self
;
323 *entry
= (wi
->entry
.path
== NULL
) ? NULL
: &wi
->entry
;
327 static int workdir_iterator__at_end(git_iterator
*self
)
329 return (((workdir_iterator
*)self
)->entry
.path
== NULL
);
332 static int workdir_iterator__advance(
333 git_iterator
*self
, const git_index_entry
**entry
)
336 workdir_iterator
*wi
= (workdir_iterator
*)self
;
337 workdir_iterator_frame
*wf
;
338 git_path_with_stat
*next
;
343 if (wi
->entry
.path
== NULL
)
346 while ((wf
= wi
->stack
) != NULL
) {
347 next
= git_vector_get(&wf
->entries
, ++wf
->index
);
349 if (strcmp(next
->path
, DOT_GIT
"/") == 0)
351 /* else found a good entry */
355 /* pop workdir directory stack */
356 wi
->stack
= wf
->next
;
357 workdir_iterator__free_frame(wf
);
358 git_ignore__pop_dir(&wi
->ignores
);
360 if (wi
->stack
== NULL
) {
361 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
366 error
= workdir_iterator__update_entry(wi
);
368 if (error
== GIT_SUCCESS
&& entry
!= NULL
)
369 error
= workdir_iterator__current(self
, entry
);
374 static int workdir_iterator__reset(git_iterator
*self
)
376 workdir_iterator
*wi
= (workdir_iterator
*)self
;
377 while (wi
->stack
!= NULL
&& wi
->stack
->next
!= NULL
) {
378 workdir_iterator_frame
*wf
= wi
->stack
;
379 wi
->stack
= wf
->next
;
380 workdir_iterator__free_frame(wf
);
381 git_ignore__pop_dir(&wi
->ignores
);
384 wi
->stack
->index
= 0;
388 static void workdir_iterator__free(git_iterator
*self
)
390 workdir_iterator
*wi
= (workdir_iterator
*)self
;
392 while (wi
->stack
!= NULL
) {
393 workdir_iterator_frame
*wf
= wi
->stack
;
394 wi
->stack
= wf
->next
;
395 workdir_iterator__free_frame(wf
);
398 git_ignore__free(&wi
->ignores
);
399 git_buf_free(&wi
->path
);
402 static int workdir_iterator__update_entry(workdir_iterator
*wi
)
405 git_path_with_stat
*ps
= git_vector_get(&wi
->stack
->entries
, wi
->stack
->index
);
407 git_buf_truncate(&wi
->path
, wi
->root_len
);
408 error
= git_buf_put(&wi
->path
, ps
->path
, ps
->path_len
);
409 if (error
< GIT_SUCCESS
)
412 memset(&wi
->entry
, 0, sizeof(wi
->entry
));
413 wi
->entry
.path
= ps
->path
;
415 /* skip over .git directory */
416 if (strcmp(ps
->path
, DOT_GIT
"/") == 0)
417 return workdir_iterator__advance((git_iterator
*)wi
, NULL
);
419 /* if there is an error processing the entry, treat as ignored */
422 /* TODO: remove shared code for struct stat conversion with index.c */
423 wi
->entry
.ctime
.seconds
= (git_time_t
)ps
->st
.st_ctime
;
424 wi
->entry
.mtime
.seconds
= (git_time_t
)ps
->st
.st_mtime
;
425 wi
->entry
.dev
= ps
->st
.st_rdev
;
426 wi
->entry
.ino
= ps
->st
.st_ino
;
427 wi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
428 wi
->entry
.uid
= ps
->st
.st_uid
;
429 wi
->entry
.gid
= ps
->st
.st_gid
;
430 wi
->entry
.file_size
= ps
->st
.st_size
;
432 /* if this is a file type we don't handle, treat as ignored */
433 if (wi
->entry
.mode
== 0)
436 /* okay, we are far enough along to look up real ignore rule */
437 error
= git_ignore__lookup(&wi
->ignores
, wi
->entry
.path
, &wi
->is_ignored
);
438 if (error
!= GIT_SUCCESS
)
441 /* detect submodules */
442 if (S_ISDIR(wi
->entry
.mode
) &&
443 git_path_contains(&wi
->path
, DOT_GIT
) == true)
444 wi
->entry
.mode
= S_IFGITLINK
;
449 int git_iterator_for_workdir(git_repository
*repo
, git_iterator
**iter
)
452 workdir_iterator
*wi
= git__calloc(1, sizeof(workdir_iterator
));
456 wi
->base
.type
= GIT_ITERATOR_WORKDIR
;
457 wi
->base
.current
= workdir_iterator__current
;
458 wi
->base
.at_end
= workdir_iterator__at_end
;
459 wi
->base
.advance
= workdir_iterator__advance
;
460 wi
->base
.reset
= workdir_iterator__reset
;
461 wi
->base
.free
= workdir_iterator__free
;
464 error
= git_buf_sets(&wi
->path
, git_repository_workdir(repo
));
465 if (error
== GIT_SUCCESS
)
466 error
= git_path_to_dir(&wi
->path
);
467 if (error
== GIT_SUCCESS
)
468 error
= git_ignore__for_path(repo
, "", &wi
->ignores
);
469 if (error
!= GIT_SUCCESS
) {
474 wi
->root_len
= wi
->path
.size
;
476 if ((error
= workdir_iterator__expand_dir(wi
)) < GIT_SUCCESS
)
477 git_iterator_free((git_iterator
*)wi
);
479 *iter
= (git_iterator
*)wi
;
485 int git_iterator_current_tree_entry(
486 git_iterator
*iter
, const git_tree_entry
**tree_entry
)
488 *tree_entry
= (iter
->type
!= GIT_ITERATOR_TREE
) ? NULL
:
489 tree_iterator__tree_entry((tree_iterator
*)iter
);
493 int git_iterator_current_is_ignored(git_iterator
*iter
)
495 return (iter
->type
!= GIT_ITERATOR_WORKDIR
) ? 0 :
496 ((workdir_iterator
*)iter
)->is_ignored
;
499 int git_iterator_advance_into_directory(
500 git_iterator
*iter
, const git_index_entry
**entry
)
502 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
504 if (iter
->type
== GIT_ITERATOR_WORKDIR
&&
505 wi
->entry
.path
&& S_ISDIR(wi
->entry
.mode
))
507 if (workdir_iterator__expand_dir(wi
) < GIT_SUCCESS
)
508 /* if error loading or if empty, skip the directory. */
509 return workdir_iterator__advance(iter
, entry
);
512 return entry
? git_iterator_current(iter
, entry
) : GIT_SUCCESS
;