]>
git.proxmox.com Git - libgit2.git/blob - src/revwalk.c
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
29 #include "hashtable.h"
32 typedef struct commit_object
{
40 unsigned short in_degree
;
41 unsigned short out_degree
;
43 struct commit_object
**parents
;
46 typedef struct commit_list
{
48 struct commit_list
*next
;
54 git_hashtable
*commits
;
57 commit_list
*iterator_topo
;
58 commit_list
*iterator_rand
;
59 commit_list
*iterator_reverse
;
60 git_pqueue iterator_time
;
62 int (*get_next
)(commit_object
**, git_revwalk
*);
63 int (*enqueue
)(git_revwalk
*, commit_object
*);
65 git_vector memory_alloc
;
72 commit_list
*commit_list_insert(commit_object
*item
, commit_list
**list_p
)
74 commit_list
*new_list
= git__malloc(sizeof(commit_list
));
75 new_list
->item
= item
;
76 new_list
->next
= *list_p
;
81 void commit_list_free(commit_list
*list
)
84 commit_list
*temp
= list
;
90 commit_object
*commit_list_pop(commit_list
**stack
)
92 commit_list
*top
= *stack
;
93 commit_object
*item
= top
? top
->item
: NULL
;
102 static int commit_time_cmp(void *a
, void *b
)
104 commit_object
*commit_a
= (commit_object
*)a
;
105 commit_object
*commit_b
= (commit_object
*)b
;
107 return (commit_a
->time
< commit_b
->time
);
110 static uint32_t object_table_hash(const void *key
, int hash_id
)
116 memcpy(&r
, id
->id
+ (hash_id
* sizeof(uint32_t)), sizeof(r
));
120 #define COMMITS_PER_CHUNK 128
121 #define CHUNK_STEP 64
122 #define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *))
124 static int alloc_chunk(git_revwalk
*walk
)
128 chunk
= git__calloc(COMMITS_PER_CHUNK
, CHUNK_STEP
);
132 walk
->chunk_size
= 0;
133 return git_vector_insert(&walk
->memory_alloc
, chunk
);
136 static commit_object
*alloc_commit(git_revwalk
*walk
)
138 unsigned char *chunk
;
140 if (walk
->chunk_size
== COMMITS_PER_CHUNK
)
143 chunk
= git_vector_get(&walk
->memory_alloc
, walk
->memory_alloc
.length
- 1);
144 chunk
+= (walk
->chunk_size
* CHUNK_STEP
);
147 return (commit_object
*)chunk
;
150 static commit_object
**alloc_parents(commit_object
*commit
, size_t n_parents
)
152 if (n_parents
<= PARENTS_PER_COMMIT
)
153 return (commit_object
**)((unsigned char *)commit
+ sizeof(commit_object
));
155 return git__malloc(n_parents
* sizeof(commit_object
*));
158 int git_revwalk_new(git_revwalk
**revwalk_out
, git_repository
*repo
)
162 walk
= git__malloc(sizeof(git_revwalk
));
166 memset(walk
, 0x0, sizeof(git_revwalk
));
168 walk
->commits
= git_hashtable_alloc(64,
170 (git_hash_keyeq_ptr
)git_oid_cmp
);
172 if (walk
->commits
== NULL
) {
177 git_vector_init(&walk
->pending
, 8, NULL
);
178 git_vector_init(&walk
->memory_alloc
, 8, NULL
);
187 void git_revwalk_free(git_revwalk
*walk
)
194 git_revwalk_reset(walk
);
195 git_hashtable_free(walk
->commits
);
196 git_vector_free(&walk
->pending
);
198 for (i
= 0; i
< walk
->memory_alloc
.length
; ++i
) {
199 free(git_vector_get(&walk
->memory_alloc
, i
));
202 git_vector_free(&walk
->memory_alloc
);
206 git_repository
*git_revwalk_repository(git_revwalk
*walk
)
212 int git_revwalk_sorting(git_revwalk
*walk
, unsigned int sort_mode
)
219 walk
->sorting
= sort_mode
;
220 git_revwalk_reset(walk
);
224 static commit_object
*commit_lookup(git_revwalk
*walk
, const git_oid
*oid
)
226 commit_object
*commit
;
228 if ((commit
= git_hashtable_lookup(walk
->commits
, oid
)) != NULL
)
231 commit
= alloc_commit(walk
);
235 git_oid_cpy(&commit
->oid
, oid
);
237 if (git_hashtable_insert(walk
->commits
, &commit
->oid
, commit
) < GIT_SUCCESS
) {
245 static int commit_quick_parse(git_revwalk
*walk
, commit_object
*commit
, git_rawobj
*raw
)
247 const int parent_len
= STRLEN("parent ") + GIT_OID_HEXSZ
+ 1;
249 unsigned char *buffer
= raw
->data
;
250 unsigned char *buffer_end
= buffer
+ raw
->len
;
251 unsigned char *parents_start
;
255 buffer
+= STRLEN("tree ") + GIT_OID_HEXSZ
+ 1;
257 parents_start
= buffer
;
258 while (buffer
+ parent_len
< buffer_end
&& memcmp(buffer
, "parent ", STRLEN("parent ")) == 0) {
260 buffer
+= parent_len
;
263 commit
->parents
= alloc_parents(commit
, parents
);
264 if (commit
->parents
== NULL
)
267 buffer
= parents_start
;
268 for (i
= 0; i
< parents
; ++i
) {
271 if (git_oid_mkstr(&oid
, (char *)buffer
+ STRLEN("parent ")) < GIT_SUCCESS
)
272 return GIT_EOBJCORRUPTED
;
274 commit
->parents
[i
] = commit_lookup(walk
, &oid
);
275 if (commit
->parents
[i
] == NULL
)
278 buffer
+= parent_len
;
281 commit
->out_degree
= (unsigned short)parents
;
283 if ((buffer
= memchr(buffer
, '\n', buffer_end
- buffer
)) == NULL
)
284 return GIT_EOBJCORRUPTED
;
286 buffer
= memchr(buffer
, '>', buffer_end
- buffer
);
288 return GIT_EOBJCORRUPTED
;
290 commit
->time
= strtol((char *)buffer
+ 2, NULL
, 10);
291 if (commit
->time
== 0)
292 return GIT_EOBJCORRUPTED
;
298 static int commit_parse(git_revwalk
*walk
, commit_object
*commit
)
306 if ((error
= git_odb_read(&data
, walk
->repo
->db
, &commit
->oid
)) < GIT_SUCCESS
)
309 if (data
.type
!= GIT_OBJ_COMMIT
) {
310 git_rawobj_close(&data
);
314 error
= commit_quick_parse(walk
, commit
, &data
);
315 git_rawobj_close(&data
);
319 static void mark_uninteresting(commit_object
*commit
)
324 commit
->uninteresting
= 1;
326 for (i
= 0; i
< commit
->out_degree
; ++i
)
327 if (!commit
->parents
[i
]->uninteresting
)
328 mark_uninteresting(commit
->parents
[i
]);
331 static int process_commit(git_revwalk
*walk
, commit_object
*commit
)
340 if ((error
= commit_parse(walk
, commit
)) < GIT_SUCCESS
)
343 if (commit
->uninteresting
)
344 mark_uninteresting(commit
);
346 return walk
->enqueue(walk
, commit
);
349 static int process_commit_parents(git_revwalk
*walk
, commit_object
*commit
)
352 int error
= GIT_SUCCESS
;
354 for (i
= 0; i
< commit
->out_degree
&& error
== GIT_SUCCESS
; ++i
) {
355 error
= process_commit(walk
, commit
->parents
[i
]);
361 static int push_commit(git_revwalk
*walk
, const git_oid
*oid
, int uninteresting
)
363 commit_object
*commit
;
365 commit
= commit_lookup(walk
, oid
);
367 return GIT_ENOTFOUND
;
370 mark_uninteresting(commit
);
372 return git_vector_insert(&walk
->pending
, commit
);
375 int git_revwalk_push(git_revwalk
*walk
, const git_oid
*oid
)
378 return push_commit(walk
, oid
, 0);
381 int git_revwalk_hide(git_revwalk
*walk
, const git_oid
*oid
)
384 return push_commit(walk
, oid
, 1);
387 static int revwalk_enqueue_timesort(git_revwalk
*walk
, commit_object
*commit
)
389 return git_pqueue_insert(&walk
->iterator_time
, commit
);
392 static int revwalk_enqueue_unsorted(git_revwalk
*walk
, commit_object
*commit
)
394 return commit_list_insert(commit
, &walk
->iterator_rand
) ? GIT_SUCCESS
: GIT_ENOMEM
;
397 static int revwalk_next_timesort(commit_object
**object_out
, git_revwalk
*walk
)
402 while ((next
= git_pqueue_pop(&walk
->iterator_time
)) != NULL
) {
403 if ((error
= process_commit_parents(walk
, next
)) < GIT_SUCCESS
)
406 if (!next
->uninteresting
) {
412 return GIT_EREVWALKOVER
;
415 static int revwalk_next_unsorted(commit_object
**object_out
, git_revwalk
*walk
)
420 while ((next
= commit_list_pop(&walk
->iterator_rand
)) != NULL
) {
421 if ((error
= process_commit_parents(walk
, next
)) < GIT_SUCCESS
)
424 if (!next
->uninteresting
) {
430 return GIT_EREVWALKOVER
;
433 static int revwalk_next_toposort(commit_object
**object_out
, git_revwalk
*walk
)
439 next
= commit_list_pop(&walk
->iterator_topo
);
441 return GIT_EREVWALKOVER
;
443 if (next
->in_degree
> 0) {
444 next
->topo_delay
= 1;
448 for (i
= 0; i
< next
->out_degree
; ++i
) {
449 commit_object
*parent
= next
->parents
[i
];
451 if (--parent
->in_degree
== 0 && parent
->topo_delay
) {
452 parent
->topo_delay
= 0;
453 commit_list_insert(parent
, &walk
->iterator_topo
);
462 static int revwalk_next_reverse(commit_object
**object_out
, git_revwalk
*walk
)
464 *object_out
= commit_list_pop(&walk
->iterator_reverse
);
465 return *object_out
? GIT_SUCCESS
: GIT_EREVWALKOVER
;
469 static int prepare_walk(git_revwalk
*walk
)
474 if (walk
->sorting
& GIT_SORT_TIME
) {
475 if ((error
= git_pqueue_init(&walk
->iterator_time
, 32, commit_time_cmp
)) < GIT_SUCCESS
)
478 walk
->get_next
= &revwalk_next_timesort
;
479 walk
->enqueue
= &revwalk_enqueue_timesort
;
481 walk
->get_next
= &revwalk_next_unsorted
;
482 walk
->enqueue
= &revwalk_enqueue_unsorted
;
485 for (i
= 0; i
< walk
->pending
.length
; ++i
) {
486 commit_object
*commit
= walk
->pending
.contents
[i
];
487 if ((error
= process_commit(walk
, commit
)) < GIT_SUCCESS
) {
492 if (walk
->sorting
& GIT_SORT_TOPOLOGICAL
) {
497 while ((error
= walk
->get_next(&next
, walk
)) == GIT_SUCCESS
) {
498 for (i
= 0; i
< next
->out_degree
; ++i
) {
499 commit_object
*parent
= next
->parents
[i
];
503 commit_list_insert(next
, &walk
->iterator_topo
);
506 if (error
!= GIT_EREVWALKOVER
)
509 walk
->get_next
= &revwalk_next_toposort
;
512 if (walk
->sorting
& GIT_SORT_REVERSE
) {
516 while ((error
= walk
->get_next(&next
, walk
)) == GIT_SUCCESS
)
517 commit_list_insert(next
, &walk
->iterator_reverse
);
519 if (error
!= GIT_EREVWALKOVER
)
522 walk
->get_next
= &revwalk_next_reverse
;
530 int git_revwalk_next(git_oid
*oid
, git_revwalk
*walk
)
537 if (!walk
->walking
) {
538 if ((error
= prepare_walk(walk
)) < GIT_SUCCESS
)
542 error
= walk
->get_next(&next
, walk
);
543 if (error
< GIT_SUCCESS
)
546 git_oid_cpy(oid
, &next
->oid
);
550 void git_revwalk_reset(git_revwalk
*walk
)
553 commit_object
*commit
;
557 GIT_HASHTABLE_FOREACH(walk
->commits
, _unused
, commit
,
559 commit
->in_degree
= 0;
560 commit
->topo_delay
= 0;
563 git_pqueue_free(&walk
->iterator_time
);
564 commit_list_free(walk
->iterator_topo
);
565 commit_list_free(walk
->iterator_rand
);
566 commit_list_free(walk
->iterator_reverse
);