Skip to content

Commit f4bf8be

Browse files
committed
path-walk: allow consumer to specify object types
We add the ability to filter the object types in the path-walk API so the callback function is called fewer times. This adds the ability to ask for the commits in a list, as well. We re-use the empty string for this set of objects because these are passed directly to the callback function instead of being part of the 'path_stack'. Future changes will add the ability to visit annotated tags. Signed-off-by: Derrick Stolee <[email protected]>
1 parent 6f93dff commit f4bf8be

File tree

5 files changed

+129
-7
lines changed

5 files changed

+129
-7
lines changed

Documentation/technical/api-path-walk.txt

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ It is also important that you do not specify the `--objects` flag for the
3939
the objects will be walked in a separate way based on those starting
4040
commits.
4141

42+
`commits`, `blobs`, `trees`::
43+
By default, these members are enabled and signal that the path-walk
44+
API should call the `path_fn` on objects of these types. Specialized
45+
applications could disable some options to make it simpler to walk
46+
the objects or to have fewer calls to `path_fn`.
47+
+
48+
While it is possible to walk only commits in this way, consumers would be
49+
better off using the revision walk API instead.
50+
4251
Examples
4352
--------
4453

path-walk.c

+29-4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ static int add_children(struct path_walk_context *ctx,
9898
if (S_ISGITLINK(entry.mode))
9999
continue;
100100

101+
/* If the caller doesn't want blobs, then don't bother. */
102+
if (!ctx->info->blobs && type == OBJ_BLOB)
103+
continue;
104+
101105
if (type == OBJ_TREE) {
102106
struct tree *child = lookup_tree(ctx->repo, &entry.oid);
103107
o = child ? &child->object : NULL;
@@ -154,9 +158,11 @@ static int walk_path(struct path_walk_context *ctx,
154158

155159
list = strmap_get(&ctx->paths_to_lists, path);
156160

157-
/* Evaluate function pointer on this data. */
158-
ret = ctx->info->path_fn(path, &list->oids, list->type,
159-
ctx->info->path_fn_data);
161+
/* Evaluate function pointer on this data, if requested. */
162+
if ((list->type == OBJ_TREE && ctx->info->trees) ||
163+
(list->type == OBJ_BLOB && ctx->info->blobs))
164+
ret = ctx->info->path_fn(path, &list->oids, list->type,
165+
ctx->info->path_fn_data);
160166

161167
/* Expand data for children. */
162168
if (list->type == OBJ_TREE) {
@@ -198,6 +204,7 @@ int walk_objects_by_path(struct path_walk_info *info)
198204
size_t commits_nr = 0, paths_nr = 0;
199205
struct commit *c;
200206
struct type_and_oid_list *root_tree_list;
207+
struct type_and_oid_list *commit_list;
201208
struct path_walk_context ctx = {
202209
.repo = info->revs->repo,
203210
.revs = info->revs,
@@ -209,6 +216,9 @@ int walk_objects_by_path(struct path_walk_info *info)
209216

210217
trace2_region_enter("path-walk", "commit-walk", info->revs->repo);
211218

219+
CALLOC_ARRAY(commit_list, 1);
220+
commit_list->type = OBJ_COMMIT;
221+
212222
/* Insert a single list for the root tree into the paths. */
213223
CALLOC_ARRAY(root_tree_list, 1);
214224
root_tree_list->type = OBJ_TREE;
@@ -219,10 +229,18 @@ int walk_objects_by_path(struct path_walk_info *info)
219229
die(_("failed to setup revision walk"));
220230

221231
while ((c = get_revision(info->revs))) {
222-
struct object_id *oid = get_commit_tree_oid(c);
232+
struct object_id *oid;
223233
struct tree *t;
224234
commits_nr++;
225235

236+
if (info->commits)
237+
oid_array_append(&commit_list->oids,
238+
&c->object.oid);
239+
240+
/* If we only care about commits, then skip trees. */
241+
if (!info->trees && !info->blobs)
242+
continue;
243+
226244
oid = get_commit_tree_oid(c);
227245
t = lookup_tree(info->revs->repo, oid);
228246

@@ -240,6 +258,13 @@ int walk_objects_by_path(struct path_walk_info *info)
240258
trace2_data_intmax("path-walk", ctx.repo, "commits", commits_nr);
241259
trace2_region_leave("path-walk", "commit-walk", info->revs->repo);
242260

261+
/* Track all commits. */
262+
if (info->commits)
263+
ret = info->path_fn("", &commit_list->oids, OBJ_COMMIT,
264+
info->path_fn_data);
265+
oid_array_clear(&commit_list->oids);
266+
free(commit_list);
267+
243268
trace2_region_enter("path-walk", "path-walk", info->revs->repo);
244269
while (!ret && ctx.path_stack.nr) {
245270
char *path = ctx.path_stack.items[ctx.path_stack.nr - 1].string;

path-walk.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,21 @@ struct path_walk_info {
3030
*/
3131
path_fn path_fn;
3232
void *path_fn_data;
33+
34+
/**
35+
* Initialize which object types the path_fn should be called on. This
36+
* could also limit the walk to skip blobs if not set.
37+
*/
38+
int commits;
39+
int trees;
40+
int blobs;
3341
};
3442

35-
#define PATH_WALK_INFO_INIT { 0 }
43+
#define PATH_WALK_INFO_INIT { \
44+
.blobs = 1, \
45+
.trees = 1, \
46+
.commits = 1, \
47+
}
3648

3749
/**
3850
* Given the configuration of 'info', walk the commits based on 'info->revs' and

t/helper/test-path-walk.c

+15-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ static const char * const path_walk_usage[] = {
1818
};
1919

2020
struct path_walk_test_data {
21+
uintmax_t commit_nr;
2122
uintmax_t tree_nr;
2223
uintmax_t blob_nr;
2324
};
@@ -29,6 +30,11 @@ static int emit_block(const char *path, struct oid_array *oids,
2930
const char *typestr;
3031

3132
switch (type) {
33+
case OBJ_COMMIT:
34+
typestr = "COMMIT";
35+
tdata->commit_nr += oids->nr;
36+
break;
37+
3238
case OBJ_TREE:
3339
typestr = "TREE";
3440
tdata->tree_nr += oids->nr;
@@ -56,6 +62,12 @@ int cmd__path_walk(int argc, const char **argv)
5662
struct path_walk_info info = PATH_WALK_INFO_INIT;
5763
struct path_walk_test_data data = { 0 };
5864
struct option options[] = {
65+
OPT_BOOL(0, "blobs", &info.blobs,
66+
N_("toggle inclusion of blob objects")),
67+
OPT_BOOL(0, "commits", &info.commits,
68+
N_("toggle inclusion of commit objects")),
69+
OPT_BOOL(0, "trees", &info.trees,
70+
N_("toggle inclusion of tree objects")),
5971
OPT_END(),
6072
};
6173

@@ -78,9 +90,10 @@ int cmd__path_walk(int argc, const char **argv)
7890

7991
res = walk_objects_by_path(&info);
8092

81-
printf("trees:%" PRIuMAX "\n"
93+
printf("commits:%" PRIuMAX "\n"
94+
"trees:%" PRIuMAX "\n"
8295
"blobs:%" PRIuMAX "\n",
83-
data.tree_nr, data.blob_nr);
96+
data.commit_nr, data.tree_nr, data.blob_nr);
8497

8598
return res;
8699
}

t/t6601-path-walk.sh

+63
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ test_expect_success 'all' '
3131
test-tool path-walk -- --all >out &&
3232
3333
cat >expect <<-EOF &&
34+
COMMIT::$(git rev-parse topic)
35+
COMMIT::$(git rev-parse base)
36+
COMMIT::$(git rev-parse base~1)
37+
COMMIT::$(git rev-parse base~2)
38+
commits:4
3439
TREE::$(git rev-parse topic^{tree})
3540
TREE::$(git rev-parse base^{tree})
3641
TREE::$(git rev-parse base~1^{tree})
@@ -57,6 +62,10 @@ test_expect_success 'topic only' '
5762
test-tool path-walk -- topic >out &&
5863
5964
cat >expect <<-EOF &&
65+
COMMIT::$(git rev-parse topic)
66+
COMMIT::$(git rev-parse base~1)
67+
COMMIT::$(git rev-parse base~2)
68+
commits:3
6069
TREE::$(git rev-parse topic^{tree})
6170
TREE::$(git rev-parse base~1^{tree})
6271
TREE::$(git rev-parse base~2^{tree})
@@ -80,6 +89,8 @@ test_expect_success 'topic, not base' '
8089
test-tool path-walk -- topic --not base >out &&
8190
8291
cat >expect <<-EOF &&
92+
COMMIT::$(git rev-parse topic)
93+
commits:1
8394
TREE::$(git rev-parse topic^{tree})
8495
TREE:left/:$(git rev-parse topic:left)
8596
TREE:right/:$(git rev-parse topic:right)
@@ -94,10 +105,62 @@ test_expect_success 'topic, not base' '
94105
test_cmp_sorted expect out
95106
'
96107

108+
test_expect_success 'topic, not base, only blobs' '
109+
test-tool path-walk --no-trees --no-commits \
110+
-- topic --not base >out &&
111+
112+
cat >expect <<-EOF &&
113+
commits:0
114+
trees:0
115+
BLOB:a:$(git rev-parse topic:a)
116+
BLOB:left/b:$(git rev-parse topic:left/b)
117+
BLOB:right/c:$(git rev-parse topic:right/c)
118+
BLOB:right/d:$(git rev-parse topic:right/d)
119+
blobs:4
120+
EOF
121+
122+
test_cmp_sorted expect out
123+
'
124+
125+
# No, this doesn't make a lot of sense for the path-walk API,
126+
# but it is possible to do.
127+
test_expect_success 'topic, not base, only commits' '
128+
test-tool path-walk --no-blobs --no-trees \
129+
-- topic --not base >out &&
130+
131+
cat >expect <<-EOF &&
132+
COMMIT::$(git rev-parse topic)
133+
commits:1
134+
trees:0
135+
blobs:0
136+
EOF
137+
138+
test_cmp_sorted expect out
139+
'
140+
141+
test_expect_success 'topic, not base, only trees' '
142+
test-tool path-walk --no-blobs --no-commits \
143+
-- topic --not base >out &&
144+
145+
cat >expect <<-EOF &&
146+
commits:0
147+
TREE::$(git rev-parse topic^{tree})
148+
TREE:left/:$(git rev-parse topic:left)
149+
TREE:right/:$(git rev-parse topic:right)
150+
trees:3
151+
blobs:0
152+
EOF
153+
154+
test_cmp_sorted expect out
155+
'
156+
97157
test_expect_success 'topic, not base, boundary' '
98158
test-tool path-walk -- --boundary topic --not base >out &&
99159
100160
cat >expect <<-EOF &&
161+
COMMIT::$(git rev-parse topic)
162+
COMMIT::$(git rev-parse base~1)
163+
commits:2
101164
TREE::$(git rev-parse topic^{tree})
102165
TREE::$(git rev-parse base~1^{tree})
103166
TREE:left/:$(git rev-parse base~1:left)

0 commit comments

Comments
 (0)