Skip to content

Commit c8e79dc

Browse files
committed
Merge branch 'mh/ref-iterators' into next
The API to iterate over all the refs (i.e. for_each_ref(), etc.) has been revamped. * mh/ref-iterators: for_each_reflog(): reimplement using iterators dir_iterator: new API for iterating over a directory tree for_each_reflog(): don't abort for bad references do_for_each_ref(): reimplement using reference iteration refs: introduce an iterator interface ref_resolves_to_object(): new function entry_resolves_to_object(): rename function from ref_resolves_to_object() get_ref_cache(): only create an instance if there is a submodule remote rm: handle symbolic refs correctly delete_refs(): add a flags argument refs: use name "prefix" consistently do_for_each_ref(): move docstring to the header file refs: remove unnecessary "extern" keywords
2 parents dd9b30f + 5605b9d commit c8e79dc

File tree

11 files changed

+1442
-324
lines changed

11 files changed

+1442
-324
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ LIB_OBJS += diff-lib.o
718718
LIB_OBJS += diff-no-index.o
719719
LIB_OBJS += diff.o
720720
LIB_OBJS += dir.o
721+
LIB_OBJS += dir-iterator.o
721722
LIB_OBJS += editor.o
722723
LIB_OBJS += entry.o
723724
LIB_OBJS += environment.o
@@ -782,6 +783,7 @@ LIB_OBJS += read-cache.o
782783
LIB_OBJS += reflog-walk.o
783784
LIB_OBJS += refs.o
784785
LIB_OBJS += refs/files-backend.o
786+
LIB_OBJS += refs/iterator.o
785787
LIB_OBJS += ref-filter.o
786788
LIB_OBJS += remote.o
787789
LIB_OBJS += replace_object.o

builtin/fetch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
806806
for (ref = stale_refs; ref; ref = ref->next)
807807
string_list_append(&refnames, ref->name);
808808

809-
result = delete_refs(&refnames);
809+
result = delete_refs(&refnames, 0);
810810
string_list_clear(&refnames, 0);
811811
}
812812

builtin/remote.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -539,10 +539,6 @@ static int add_branch_for_removal(const char *refname,
539539
return 0;
540540
}
541541

542-
/* make sure that symrefs are deleted */
543-
if (flags & REF_ISSYMREF)
544-
return unlink(git_path("%s", refname));
545-
546542
string_list_append(branches->branches, refname);
547543

548544
return 0;
@@ -788,7 +784,7 @@ static int rm(int argc, const char **argv)
788784
strbuf_release(&buf);
789785

790786
if (!result)
791-
result = delete_refs(&branches);
787+
result = delete_refs(&branches, REF_NODEREF);
792788
string_list_clear(&branches, 0);
793789

794790
if (skipped.nr) {
@@ -1305,7 +1301,7 @@ static int prune_remote(const char *remote, int dry_run)
13051301
string_list_sort(&refs_to_prune);
13061302

13071303
if (!dry_run)
1308-
result |= delete_refs(&refs_to_prune);
1304+
result |= delete_refs(&refs_to_prune, 0);
13091305

13101306
for_each_string_list_item(item, &states.stale) {
13111307
const char *refname = item->util;

dir-iterator.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#include "cache.h"
2+
#include "dir.h"
3+
#include "iterator.h"
4+
#include "dir-iterator.h"
5+
6+
struct dir_iterator_level {
7+
int initialized;
8+
9+
DIR *dir;
10+
11+
/*
12+
* The length of the directory part of path at this level
13+
* (including a trailing '/'):
14+
*/
15+
size_t prefix_len;
16+
17+
/*
18+
* The last action that has been taken with the current entry
19+
* (needed for directories, which have to be included in the
20+
* iteration and also iterated into):
21+
*/
22+
enum {
23+
DIR_STATE_ITER,
24+
DIR_STATE_RECURSE
25+
} dir_state;
26+
};
27+
28+
/*
29+
* The full data structure used to manage the internal directory
30+
* iteration state. It includes members that are not part of the
31+
* public interface.
32+
*/
33+
struct dir_iterator_int {
34+
struct dir_iterator base;
35+
36+
/*
37+
* The number of levels currently on the stack. This is always
38+
* at least 1, because when it becomes zero the iteration is
39+
* ended and this struct is freed.
40+
*/
41+
size_t levels_nr;
42+
43+
/* The number of levels that have been allocated on the stack */
44+
size_t levels_alloc;
45+
46+
/*
47+
* A stack of levels. levels[0] is the uppermost directory
48+
* that will be included in this iteration.
49+
*/
50+
struct dir_iterator_level *levels;
51+
};
52+
53+
int dir_iterator_advance(struct dir_iterator *dir_iterator)
54+
{
55+
struct dir_iterator_int *iter =
56+
(struct dir_iterator_int *)dir_iterator;
57+
58+
while (1) {
59+
struct dir_iterator_level *level =
60+
&iter->levels[iter->levels_nr - 1];
61+
struct dirent *de;
62+
63+
if (!level->initialized) {
64+
if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1]))
65+
strbuf_addch(&iter->base.path, '/');
66+
level->prefix_len = iter->base.path.len;
67+
68+
/* opendir() errors are handled below */
69+
level->dir = opendir(iter->base.path.buf);
70+
71+
level->initialized = 1;
72+
} else if (S_ISDIR(iter->base.st.st_mode)) {
73+
if (level->dir_state == DIR_STATE_ITER) {
74+
/*
75+
* The directory was just iterated
76+
* over; now prepare to iterate into
77+
* it.
78+
*/
79+
level->dir_state = DIR_STATE_RECURSE;
80+
ALLOC_GROW(iter->levels, iter->levels_nr + 1,
81+
iter->levels_alloc);
82+
level = &iter->levels[iter->levels_nr++];
83+
level->initialized = 0;
84+
continue;
85+
} else {
86+
/*
87+
* The directory has already been
88+
* iterated over and iterated into;
89+
* we're done with it.
90+
*/
91+
}
92+
}
93+
94+
if (!level->dir) {
95+
/*
96+
* This level is exhausted (or wasn't opened
97+
* successfully); pop up a level.
98+
*/
99+
if (--iter->levels_nr == 0) {
100+
return dir_iterator_abort(dir_iterator);
101+
}
102+
continue;
103+
}
104+
105+
/*
106+
* Loop until we find an entry that we can give back
107+
* to the caller:
108+
*/
109+
while (1) {
110+
strbuf_setlen(&iter->base.path, level->prefix_len);
111+
de = readdir(level->dir);
112+
113+
if (!de) {
114+
/* This level is exhausted; pop up a level. */
115+
closedir(level->dir);
116+
level->dir = NULL;
117+
if (--iter->levels_nr == 0)
118+
return dir_iterator_abort(dir_iterator);
119+
break;
120+
}
121+
122+
if (is_dot_or_dotdot(de->d_name))
123+
continue;
124+
125+
strbuf_addstr(&iter->base.path, de->d_name);
126+
if (lstat(iter->base.path.buf, &iter->base.st) < 0) {
127+
if (errno != ENOENT)
128+
warning("error reading path '%s': %s",
129+
iter->base.path.buf,
130+
strerror(errno));
131+
continue;
132+
}
133+
134+
/*
135+
* We have to set these each time because
136+
* the path strbuf might have been realloc()ed.
137+
*/
138+
139+
iter->base.relative_path =
140+
iter->base.path.buf + iter->levels[0].prefix_len;
141+
iter->base.basename =
142+
iter->base.path.buf + level->prefix_len;
143+
level->dir_state = DIR_STATE_ITER;
144+
145+
return ITER_OK;
146+
}
147+
}
148+
}
149+
150+
int dir_iterator_abort(struct dir_iterator *dir_iterator)
151+
{
152+
struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator;
153+
154+
while (iter->levels_nr) {
155+
struct dir_iterator_level *level =
156+
&iter->levels[--iter->levels_nr];
157+
158+
if (level->dir)
159+
closedir(level->dir);
160+
}
161+
162+
free(iter->levels);
163+
strbuf_release(&iter->base.path);
164+
free(iter);
165+
return ITER_DONE;
166+
}
167+
168+
struct dir_iterator *dir_iterator_begin(const char *path)
169+
{
170+
struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
171+
struct dir_iterator *dir_iterator = &iter->base;
172+
173+
if (!path || !*path)
174+
die("BUG: empty path passed to dir_iterator_begin()");
175+
176+
strbuf_init(&iter->base.path, PATH_MAX);
177+
strbuf_addstr(&iter->base.path, path);
178+
179+
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
180+
181+
iter->levels_nr = 1;
182+
iter->levels[0].initialized = 0;
183+
184+
return dir_iterator;
185+
}

dir-iterator.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#ifndef DIR_ITERATOR_H
2+
#define DIR_ITERATOR_H
3+
4+
/*
5+
* Iterate over a directory tree.
6+
*
7+
* Iterate over a directory tree, recursively, including paths of all
8+
* types and hidden paths. Skip "." and ".." entries and don't follow
9+
* symlinks except for the original path.
10+
*
11+
* Every time dir_iterator_advance() is called, update the members of
12+
* the dir_iterator structure to reflect the next path in the
13+
* iteration. The order that paths are iterated over within a
14+
* directory is undefined, but directory paths are always iterated
15+
* over before the subdirectory contents.
16+
*
17+
* A typical iteration looks like this:
18+
*
19+
* int ok;
20+
* struct iterator *iter = dir_iterator_begin(path);
21+
*
22+
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
23+
* if (want_to_stop_iteration()) {
24+
* ok = dir_iterator_abort(iter);
25+
* break;
26+
* }
27+
*
28+
* // Access information about the current path:
29+
* if (S_ISDIR(iter->st.st_mode))
30+
* printf("%s is a directory\n", iter->relative_path);
31+
* }
32+
*
33+
* if (ok != ITER_DONE)
34+
* handle_error();
35+
*
36+
* Callers are allowed to modify iter->path while they are working,
37+
* but they must restore it to its original contents before calling
38+
* dir_iterator_advance() again.
39+
*/
40+
41+
struct dir_iterator {
42+
/* The current path: */
43+
struct strbuf path;
44+
45+
/*
46+
* The current path relative to the starting path. This part
47+
* of the path always uses "/" characters to separate path
48+
* components:
49+
*/
50+
const char *relative_path;
51+
52+
/* The current basename: */
53+
const char *basename;
54+
55+
/* The result of calling lstat() on path: */
56+
struct stat st;
57+
};
58+
59+
/*
60+
* Start a directory iteration over path. Return a dir_iterator that
61+
* holds the internal state of the iteration.
62+
*
63+
* The iteration includes all paths under path, not including path
64+
* itself and not including "." or ".." entries.
65+
*
66+
* path is the starting directory. An internal copy will be made.
67+
*/
68+
struct dir_iterator *dir_iterator_begin(const char *path);
69+
70+
/*
71+
* Advance the iterator to the first or next item and return ITER_OK.
72+
* If the iteration is exhausted, free the resources associated with
73+
* the iterator and return ITER_DONE. On error, return ITER_ERROR. It
74+
* is a bug to use iterator or call this function again after it has
75+
* returned false.
76+
*/
77+
int dir_iterator_advance(struct dir_iterator *iterator);
78+
79+
/*
80+
* End the iteration before it has been exhausted. Free the directory
81+
* iterator and any associated resources and return ITER_DONE. Return
82+
* ITER_ERROR on error.
83+
*/
84+
int dir_iterator_abort(struct dir_iterator *iterator);
85+
86+
#endif

0 commit comments

Comments
 (0)