]>
git.proxmox.com Git - libgit2.git/blob - src/revwalk.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
14 #include "git2/revparse.h"
17 git_commit_list_node
*git_revwalk__commit_lookup(
18 git_revwalk
*walk
, const git_oid
*oid
)
20 git_commit_list_node
*commit
;
24 /* lookup and reserve space if not already present */
25 pos
= kh_get(oid
, walk
->commits
, oid
);
26 if (pos
!= kh_end(walk
->commits
))
27 return kh_value(walk
->commits
, pos
);
29 commit
= git_commit_list_alloc_node(walk
);
33 git_oid_cpy(&commit
->oid
, oid
);
35 pos
= kh_put(oid
, walk
->commits
, &commit
->oid
, &ret
);
37 kh_value(walk
->commits
, pos
) = commit
;
42 static int mark_uninteresting(git_commit_list_node
*commit
)
45 git_array_t(git_commit_list_node
*) pending
= GIT_ARRAY_INIT
;
46 git_commit_list_node
**tmp
;
50 git_array_alloc(pending
);
51 GITERR_CHECK_ARRAY(pending
);
54 commit
->uninteresting
= 1;
56 /* This means we've reached a merge base, so there's no need to walk any more */
57 if ((commit
->flags
& (RESULT
| STALE
)) == RESULT
) {
58 tmp
= git_array_pop(pending
);
59 commit
= tmp
? *tmp
: NULL
;
63 for (i
= 0; i
< commit
->out_degree
; ++i
)
64 if (!commit
->parents
[i
]->uninteresting
) {
65 git_commit_list_node
**node
= git_array_alloc(pending
);
66 GITERR_CHECK_ALLOC(node
);
67 *node
= commit
->parents
[i
];
70 tmp
= git_array_pop(pending
);
71 commit
= tmp
? *tmp
: NULL
;
73 } while (git_array_size(pending
) > 0);
75 git_array_clear(pending
);
80 static int process_commit(git_revwalk
*walk
, git_commit_list_node
*commit
, int hide
)
84 if (hide
&& mark_uninteresting(commit
) < 0)
92 if ((error
= git_commit_list_parse(walk
, commit
)) < 0)
95 return walk
->enqueue(walk
, commit
);
98 static int process_commit_parents(git_revwalk
*walk
, git_commit_list_node
*commit
)
100 unsigned short i
, max
;
103 max
= commit
->out_degree
;
104 if (walk
->first_parent
&& commit
->out_degree
)
107 for (i
= 0; i
< max
&& !error
; ++i
)
108 error
= process_commit(walk
, commit
->parents
[i
], commit
->uninteresting
);
113 static int push_commit(git_revwalk
*walk
, const git_oid
*oid
, int uninteresting
)
117 git_commit_list_node
*commit
;
119 if (git_object_lookup(&obj
, walk
->repo
, oid
, GIT_OBJ_ANY
) < 0)
122 type
= git_object_type(obj
);
123 git_object_free(obj
);
125 if (type
!= GIT_OBJ_COMMIT
) {
126 giterr_set(GITERR_INVALID
, "Object is no commit object");
130 commit
= git_revwalk__commit_lookup(walk
, oid
);
132 return -1; /* error already reported by failed lookup */
134 commit
->uninteresting
= uninteresting
;
135 if (walk
->one
== NULL
&& !uninteresting
) {
138 if (git_vector_insert(&walk
->twos
, commit
) < 0)
145 int git_revwalk_push(git_revwalk
*walk
, const git_oid
*oid
)
148 return push_commit(walk
, oid
, 0);
152 int git_revwalk_hide(git_revwalk
*walk
, const git_oid
*oid
)
155 return push_commit(walk
, oid
, 1);
158 static int push_ref(git_revwalk
*walk
, const char *refname
, int hide
)
162 if (git_reference_name_to_id(&oid
, walk
->repo
, refname
) < 0)
165 return push_commit(walk
, &oid
, hide
);
168 struct push_cb_data
{
173 static int push_glob_cb(const char *refname
, void *data_
)
175 struct push_cb_data
*data
= (struct push_cb_data
*)data_
;
177 return push_ref(data
->walk
, refname
, data
->hide
);
180 static int push_glob(git_revwalk
*walk
, const char *glob
, int hide
)
183 git_buf buf
= GIT_BUF_INIT
;
184 struct push_cb_data data
;
187 assert(walk
&& glob
);
189 /* refs/ is implied if not given in the glob */
190 if (git__prefixcmp(glob
, GIT_REFS_DIR
) != 0)
191 git_buf_joinpath(&buf
, GIT_REFS_DIR
, glob
);
193 git_buf_puts(&buf
, glob
);
195 /* If no '?', '*' or '[' exist, we append '/ *' to the glob */
196 wildcard
= strcspn(glob
, "?*[");
198 git_buf_put(&buf
, "/*", 2);
203 if (git_buf_oom(&buf
))
206 error
= git_reference_foreach_glob(
207 walk
->repo
, git_buf_cstr(&buf
), push_glob_cb
, &data
);
213 int git_revwalk_push_glob(git_revwalk
*walk
, const char *glob
)
215 assert(walk
&& glob
);
216 return push_glob(walk
, glob
, 0);
219 int git_revwalk_hide_glob(git_revwalk
*walk
, const char *glob
)
221 assert(walk
&& glob
);
222 return push_glob(walk
, glob
, 1);
225 int git_revwalk_push_head(git_revwalk
*walk
)
228 return push_ref(walk
, GIT_HEAD_FILE
, 0);
231 int git_revwalk_hide_head(git_revwalk
*walk
)
234 return push_ref(walk
, GIT_HEAD_FILE
, 1);
237 int git_revwalk_push_ref(git_revwalk
*walk
, const char *refname
)
239 assert(walk
&& refname
);
240 return push_ref(walk
, refname
, 0);
243 int git_revwalk_push_range(git_revwalk
*walk
, const char *range
)
248 if ((error
= git_revparse(&revspec
, walk
->repo
, range
)))
251 if (revspec
.flags
& GIT_REVPARSE_MERGE_BASE
) {
252 /* TODO: support "<commit>...<commit>" */
253 giterr_set(GITERR_INVALID
, "Symmetric differences not implemented in revwalk");
254 return GIT_EINVALIDSPEC
;
257 if ((error
= push_commit(walk
, git_object_id(revspec
.from
), 1)))
260 error
= push_commit(walk
, git_object_id(revspec
.to
), 0);
263 git_object_free(revspec
.from
);
264 git_object_free(revspec
.to
);
268 int git_revwalk_hide_ref(git_revwalk
*walk
, const char *refname
)
270 assert(walk
&& refname
);
271 return push_ref(walk
, refname
, 1);
274 static int revwalk_enqueue_timesort(git_revwalk
*walk
, git_commit_list_node
*commit
)
276 return git_pqueue_insert(&walk
->iterator_time
, commit
);
279 static int revwalk_enqueue_unsorted(git_revwalk
*walk
, git_commit_list_node
*commit
)
281 return git_commit_list_insert(commit
, &walk
->iterator_rand
) ? 0 : -1;
284 static int revwalk_next_timesort(git_commit_list_node
**object_out
, git_revwalk
*walk
)
287 git_commit_list_node
*next
;
289 while ((next
= git_pqueue_pop(&walk
->iterator_time
)) != NULL
) {
290 if ((error
= process_commit_parents(walk
, next
)) < 0)
293 if (!next
->uninteresting
) {
303 static int revwalk_next_unsorted(git_commit_list_node
**object_out
, git_revwalk
*walk
)
306 git_commit_list_node
*next
;
308 while ((next
= git_commit_list_pop(&walk
->iterator_rand
)) != NULL
) {
309 if ((error
= process_commit_parents(walk
, next
)) < 0)
312 if (!next
->uninteresting
) {
322 static int revwalk_next_toposort(git_commit_list_node
**object_out
, git_revwalk
*walk
)
324 git_commit_list_node
*next
;
325 unsigned short i
, max
;
328 next
= git_commit_list_pop(&walk
->iterator_topo
);
334 if (next
->in_degree
> 0) {
335 next
->topo_delay
= 1;
340 max
= next
->out_degree
;
341 if (walk
->first_parent
&& next
->out_degree
)
344 for (i
= 0; i
< max
; ++i
) {
345 git_commit_list_node
*parent
= next
->parents
[i
];
347 if (--parent
->in_degree
== 0 && parent
->topo_delay
) {
348 parent
->topo_delay
= 0;
349 if (git_commit_list_insert(parent
, &walk
->iterator_topo
) == NULL
)
359 static int revwalk_next_reverse(git_commit_list_node
**object_out
, git_revwalk
*walk
)
361 *object_out
= git_commit_list_pop(&walk
->iterator_reverse
);
362 return *object_out
? 0 : GIT_ITEROVER
;
366 static int prepare_walk(git_revwalk
*walk
)
370 git_commit_list_node
*next
, *two
;
371 git_commit_list
*bases
= NULL
;
374 * If walk->one is NULL, there were no positive references,
375 * so we know that the walk is already over.
377 if (walk
->one
== NULL
) {
382 /* first figure out what the merge bases are */
383 if (git_merge__bases_many(&bases
, walk
, walk
->one
, &walk
->twos
) < 0)
386 git_commit_list_free(&bases
);
387 if (process_commit(walk
, walk
->one
, walk
->one
->uninteresting
) < 0)
390 git_vector_foreach(&walk
->twos
, i
, two
) {
391 if (process_commit(walk
, two
, two
->uninteresting
) < 0)
395 if (walk
->sorting
& GIT_SORT_TOPOLOGICAL
) {
398 while ((error
= walk
->get_next(&next
, walk
)) == 0) {
399 for (i
= 0; i
< next
->out_degree
; ++i
) {
400 git_commit_list_node
*parent
= next
->parents
[i
];
404 if (git_commit_list_insert(next
, &walk
->iterator_topo
) == NULL
)
408 if (error
!= GIT_ITEROVER
)
411 walk
->get_next
= &revwalk_next_toposort
;
414 if (walk
->sorting
& GIT_SORT_REVERSE
) {
416 while ((error
= walk
->get_next(&next
, walk
)) == 0)
417 if (git_commit_list_insert(next
, &walk
->iterator_reverse
) == NULL
)
420 if (error
!= GIT_ITEROVER
)
423 walk
->get_next
= &revwalk_next_reverse
;
431 int git_revwalk_new(git_revwalk
**revwalk_out
, git_repository
*repo
)
435 walk
= git__malloc(sizeof(git_revwalk
));
436 GITERR_CHECK_ALLOC(walk
);
438 memset(walk
, 0x0, sizeof(git_revwalk
));
440 walk
->commits
= git_oidmap_alloc();
441 GITERR_CHECK_ALLOC(walk
->commits
);
443 if (git_pqueue_init(&walk
->iterator_time
, 8, git_commit_list_time_cmp
) < 0 ||
444 git_vector_init(&walk
->twos
, 4, NULL
) < 0 ||
445 git_pool_init(&walk
->commit_pool
, 1,
446 git_pool__suggest_items_per_page(COMMIT_ALLOC
) * COMMIT_ALLOC
) < 0)
449 walk
->get_next
= &revwalk_next_unsorted
;
450 walk
->enqueue
= &revwalk_enqueue_unsorted
;
454 if (git_repository_odb(&walk
->odb
, repo
) < 0) {
455 git_revwalk_free(walk
);
463 void git_revwalk_free(git_revwalk
*walk
)
468 git_revwalk_reset(walk
);
469 git_odb_free(walk
->odb
);
471 git_oidmap_free(walk
->commits
);
472 git_pool_clear(&walk
->commit_pool
);
473 git_pqueue_free(&walk
->iterator_time
);
474 git_vector_free(&walk
->twos
);
478 git_repository
*git_revwalk_repository(git_revwalk
*walk
)
484 void git_revwalk_sorting(git_revwalk
*walk
, unsigned int sort_mode
)
489 git_revwalk_reset(walk
);
491 walk
->sorting
= sort_mode
;
493 if (walk
->sorting
& GIT_SORT_TIME
) {
494 walk
->get_next
= &revwalk_next_timesort
;
495 walk
->enqueue
= &revwalk_enqueue_timesort
;
497 walk
->get_next
= &revwalk_next_unsorted
;
498 walk
->enqueue
= &revwalk_enqueue_unsorted
;
502 void git_revwalk_simplify_first_parent(git_revwalk
*walk
)
504 walk
->first_parent
= 1;
507 int git_revwalk_next(git_oid
*oid
, git_revwalk
*walk
)
510 git_commit_list_node
*next
;
514 if (!walk
->walking
) {
515 if ((error
= prepare_walk(walk
)) < 0)
519 error
= walk
->get_next(&next
, walk
);
521 if (error
== GIT_ITEROVER
) {
522 git_revwalk_reset(walk
);
528 git_oid_cpy(oid
, &next
->oid
);
533 void git_revwalk_reset(git_revwalk
*walk
)
535 git_commit_list_node
*commit
;
539 kh_foreach_value(walk
->commits
, commit
, {
541 commit
->in_degree
= 0;
542 commit
->topo_delay
= 0;
543 commit
->uninteresting
= 0;
546 git_pqueue_clear(&walk
->iterator_time
);
547 git_commit_list_free(&walk
->iterator_topo
);
548 git_commit_list_free(&walk
->iterator_rand
);
549 git_commit_list_free(&walk
->iterator_reverse
);
553 git_vector_clear(&walk
->twos
);