Skip to content

Commit 6fedcc4

Browse files
derrickstoleedscho
authored andcommitted
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. Future changes will add the ability to visit annotated tags. Signed-off-by: Derrick Stolee <[email protected]>
1 parent 65b5595 commit 6fedcc4

File tree

5 files changed

+141
-9
lines changed

5 files changed

+141
-9
lines changed

Documentation/technical/api-path-walk.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ If you want the path-walk API to emit `UNINTERESTING` objects based on the
4848
commit walk's boundary, be sure to set `revs.boundary` so the boundary
4949
commits are emitted.
5050

51+
`commits`, `blobs`, `trees`::
52+
By default, these members are enabled and signal that the path-walk
53+
API should call the `path_fn` on objects of these types. Specialized
54+
applications could disable some options to make it simpler to walk
55+
the objects or to have fewer calls to `path_fn`.
56+
+
57+
While it is possible to walk only commits in this way, consumers would be
58+
better off using the revision walk API instead.
59+
5160
Examples
5261
--------
5362

path-walk.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ static int add_children(struct path_walk_context *ctx,
8484
if (S_ISGITLINK(entry.mode))
8585
continue;
8686

87+
/* If the caller doesn't want blobs, then don't bother. */
88+
if (!ctx->info->blobs && type == OBJ_BLOB)
89+
continue;
90+
8791
if (type == OBJ_TREE) {
8892
struct tree *child = lookup_tree(ctx->repo, &entry.oid);
8993
o = child ? &child->object : NULL;
@@ -140,9 +144,11 @@ static int walk_path(struct path_walk_context *ctx,
140144

141145
list = strmap_get(&ctx->paths_to_lists, path);
142146

143-
/* Evaluate function pointer on this data. */
144-
ret = ctx->info->path_fn(path, &list->oids, list->type,
145-
ctx->info->path_fn_data);
147+
/* Evaluate function pointer on this data, if requested. */
148+
if ((list->type == OBJ_TREE && ctx->info->trees) ||
149+
(list->type == OBJ_BLOB && ctx->info->blobs))
150+
ret = ctx->info->path_fn(path, &list->oids, list->type,
151+
ctx->info->path_fn_data);
146152

147153
/* Expand data for children. */
148154
if (list->type == OBJ_TREE) {
@@ -184,6 +190,7 @@ int walk_objects_by_path(struct path_walk_info *info)
184190
size_t commits_nr = 0, paths_nr = 0;
185191
struct commit *c;
186192
struct type_and_oid_list *root_tree_list;
193+
struct type_and_oid_list *commit_list;
187194
struct path_walk_context ctx = {
188195
.repo = info->revs->repo,
189196
.revs = info->revs,
@@ -194,19 +201,32 @@ int walk_objects_by_path(struct path_walk_info *info)
194201

195202
trace2_region_enter("path-walk", "commit-walk", info->revs->repo);
196203

204+
CALLOC_ARRAY(commit_list, 1);
205+
commit_list->type = OBJ_COMMIT;
206+
197207
/* Insert a single list for the root tree into the paths. */
198208
CALLOC_ARRAY(root_tree_list, 1);
199209
root_tree_list->type = OBJ_TREE;
200210
strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
201-
202211
if (prepare_revision_walk(info->revs))
203212
die(_("failed to setup revision walk"));
204213

205214
while ((c = get_revision(info->revs))) {
206-
struct object_id *oid = get_commit_tree_oid(c);
207-
struct tree *t = lookup_tree(info->revs->repo, oid);
215+
struct object_id *oid;
216+
struct tree *t;
208217
commits_nr++;
209218

219+
if (info->commits)
220+
oid_array_append(&commit_list->oids,
221+
&c->object.oid);
222+
223+
/* If we only care about commits, then skip trees. */
224+
if (!info->trees && !info->blobs)
225+
continue;
226+
227+
oid = get_commit_tree_oid(c);
228+
t = lookup_tree(info->revs->repo, oid);
229+
210230
if (t)
211231
oid_array_append(&root_tree_list->oids, oid);
212232
else
@@ -216,6 +236,13 @@ int walk_objects_by_path(struct path_walk_info *info)
216236
trace2_data_intmax("path-walk", ctx.repo, "commits", commits_nr);
217237
trace2_region_leave("path-walk", "commit-walk", info->revs->repo);
218238

239+
/* Track all commits. */
240+
if (info->commits)
241+
ret = info->path_fn("", &commit_list->oids, OBJ_COMMIT,
242+
info->path_fn_data);
243+
oid_array_clear(&commit_list->oids);
244+
free(commit_list);
245+
219246
string_list_append(&ctx.path_stack, root_path);
220247

221248
trace2_region_enter("path-walk", "path-walk", info->revs->repo);

path-walk.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,20 @@ struct path_walk_info {
3030
*/
3131
path_fn path_fn;
3232
void *path_fn_data;
33+
/**
34+
* Initialize which object types the path_fn should be called on. This
35+
* could also limit the walk to skip blobs if not set.
36+
*/
37+
int commits;
38+
int trees;
39+
int blobs;
3340
};
3441

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

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

t/helper/test-path-walk.c

Lines changed: 15 additions & 2 deletions
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

Lines changed: 72 additions & 0 deletions
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})
@@ -60,6 +65,10 @@ test_expect_success 'topic only' '
6065
test-tool path-walk -- topic >out &&
6166
6267
cat >expect <<-EOF &&
68+
COMMIT::$(git rev-parse topic)
69+
COMMIT::$(git rev-parse base~1)
70+
COMMIT::$(git rev-parse base~2)
71+
commits:3
6372
TREE::$(git rev-parse topic^{tree})
6473
TREE::$(git rev-parse base~1^{tree})
6574
TREE::$(git rev-parse base~2^{tree})
@@ -86,6 +95,8 @@ test_expect_success 'topic, not base' '
8695
test-tool path-walk -- topic --not base >out &&
8796
8897
cat >expect <<-EOF &&
98+
COMMIT::$(git rev-parse topic)
99+
commits:1
89100
TREE::$(git rev-parse topic^{tree})
90101
TREE:left/:$(git rev-parse topic:left)
91102
TREE:right/:$(git rev-parse topic:right)
@@ -103,10 +114,71 @@ test_expect_success 'topic, not base' '
103114
test_cmp expect.sorted out.sorted
104115
'
105116

117+
test_expect_success 'topic, not base, only blobs' '
118+
test-tool path-walk --no-trees --no-commits \
119+
-- topic --not base >out &&
120+
121+
cat >expect <<-EOF &&
122+
commits:0
123+
trees:0
124+
BLOB:a:$(git rev-parse topic:a)
125+
BLOB:left/b:$(git rev-parse topic:left/b)
126+
BLOB:right/c:$(git rev-parse topic:right/c)
127+
BLOB:right/d:$(git rev-parse topic:right/d)
128+
blobs:4
129+
EOF
130+
131+
sort expect >expect.sorted &&
132+
sort out >out.sorted &&
133+
134+
test_cmp expect.sorted out.sorted
135+
'
136+
137+
# No, this doesn't make a lot of sense for the path-walk API,
138+
# but it is possible to do.
139+
test_expect_success 'topic, not base, only commits' '
140+
test-tool path-walk --no-blobs --no-trees \
141+
-- topic --not base >out &&
142+
143+
cat >expect <<-EOF &&
144+
COMMIT::$(git rev-parse topic)
145+
commits:1
146+
trees:0
147+
blobs:0
148+
EOF
149+
150+
sort expect >expect.sorted &&
151+
sort out >out.sorted &&
152+
153+
test_cmp expect.sorted out.sorted
154+
'
155+
156+
test_expect_success 'topic, not base, only trees' '
157+
test-tool path-walk --no-blobs --no-commits \
158+
-- topic --not base >out &&
159+
160+
cat >expect <<-EOF &&
161+
commits:0
162+
TREE::$(git rev-parse topic^{tree})
163+
TREE:left/:$(git rev-parse topic:left)
164+
TREE:right/:$(git rev-parse topic:right)
165+
trees:3
166+
blobs:0
167+
EOF
168+
169+
sort expect >expect.sorted &&
170+
sort out >out.sorted &&
171+
172+
test_cmp expect.sorted out.sorted
173+
'
174+
106175
test_expect_success 'topic, not base, boundary' '
107176
test-tool path-walk -- --boundary topic --not base >out &&
108177
109178
cat >expect <<-EOF &&
179+
COMMIT::$(git rev-parse topic)
180+
COMMIT::$(git rev-parse base~1)
181+
commits:2
110182
TREE::$(git rev-parse topic^{tree})
111183
TREE::$(git rev-parse base~1^{tree})
112184
TREE:left/:$(git rev-parse base~1:left)

0 commit comments

Comments
 (0)