]>
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 #include "git2/revwalk.h"
34 typedef struct commit_object
{
42 unsigned short in_degree
;
43 unsigned short out_degree
;
45 struct commit_object
**parents
;
48 typedef struct commit_list
{
50 struct commit_list
*next
;
56 git_hashtable
*commits
;
58 commit_list
*iterator_topo
;
59 commit_list
*iterator_rand
;
60 commit_list
*iterator_reverse
;
61 git_pqueue iterator_time
;
63 int (*get_next
)(commit_object
**, git_revwalk
*);
64 int (*enqueue
)(git_revwalk
*, commit_object
*);
66 git_vector memory_alloc
;
73 commit_list
*commit_list_insert(commit_object
*item
, commit_list
**list_p
)
75 commit_list
*new_list
= git__malloc(sizeof(commit_list
));
76 new_list
->item
= item
;
77 new_list
->next
= *list_p
;
82 void commit_list_free(commit_list
**list_p
)
84 commit_list
*list
= *list_p
;
87 commit_list
*temp
= list
;
95 commit_object
*commit_list_pop(commit_list
**stack
)
97 commit_list
*top
= *stack
;
98 commit_object
*item
= top
? top
->item
: NULL
;
107 static int commit_time_cmp(void *a
, void *b
)
109 commit_object
*commit_a
= (commit_object
*)a
;
110 commit_object
*commit_b
= (commit_object
*)b
;
112 return (commit_a
->time
< commit_b
->time
);
115 static uint32_t object_table_hash(const void *key
, int hash_id
)
121 memcpy(&r
, id
->id
+ (hash_id
* sizeof(uint32_t)), sizeof(r
));
125 #define COMMITS_PER_CHUNK 128
126 #define CHUNK_STEP 64
127 #define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *))
129 static int alloc_chunk(git_revwalk
*walk
)
133 chunk
= git__calloc(COMMITS_PER_CHUNK
, CHUNK_STEP
);
137 walk
->chunk_size
= 0;
138 return git_vector_insert(&walk
->memory_alloc
, chunk
);
141 static commit_object
*alloc_commit(git_revwalk
*walk
)
143 unsigned char *chunk
;
145 if (walk
->chunk_size
== COMMITS_PER_CHUNK
)
148 chunk
= git_vector_get(&walk
->memory_alloc
, walk
->memory_alloc
.length
- 1);
149 chunk
+= (walk
->chunk_size
* CHUNK_STEP
);
152 return (commit_object
*)chunk
;
155 static commit_object
**alloc_parents(commit_object
*commit
, size_t n_parents
)
157 if (n_parents
<= PARENTS_PER_COMMIT
)
158 return (commit_object
**)((unsigned char *)commit
+ sizeof(commit_object
));
160 return git__malloc(n_parents
* sizeof(commit_object
*));
164 static commit_object
*commit_lookup(git_revwalk
*walk
, const git_oid
*oid
)
166 commit_object
*commit
;
168 if ((commit
= git_hashtable_lookup(walk
->commits
, oid
)) != NULL
)
171 commit
= alloc_commit(walk
);
175 git_oid_cpy(&commit
->oid
, oid
);
177 if (git_hashtable_insert(walk
->commits
, &commit
->oid
, commit
) < GIT_SUCCESS
) {
185 static int commit_quick_parse(git_revwalk
*walk
, commit_object
*commit
, git_rawobj
*raw
)
187 const int parent_len
= STRLEN("parent ") + GIT_OID_HEXSZ
+ 1;
189 unsigned char *buffer
= raw
->data
;
190 unsigned char *buffer_end
= buffer
+ raw
->len
;
191 unsigned char *parents_start
;
195 buffer
+= STRLEN("tree ") + GIT_OID_HEXSZ
+ 1;
197 parents_start
= buffer
;
198 while (buffer
+ parent_len
< buffer_end
&& memcmp(buffer
, "parent ", STRLEN("parent ")) == 0) {
200 buffer
+= parent_len
;
203 commit
->parents
= alloc_parents(commit
, parents
);
204 if (commit
->parents
== NULL
)
207 buffer
= parents_start
;
208 for (i
= 0; i
< parents
; ++i
) {
211 if (git_oid_mkstr(&oid
, (char *)buffer
+ STRLEN("parent ")) < GIT_SUCCESS
)
212 return GIT_EOBJCORRUPTED
;
214 commit
->parents
[i
] = commit_lookup(walk
, &oid
);
215 if (commit
->parents
[i
] == NULL
)
218 buffer
+= parent_len
;
221 commit
->out_degree
= (unsigned short)parents
;
223 if ((buffer
= memchr(buffer
, '\n', buffer_end
- buffer
)) == NULL
)
224 return GIT_EOBJCORRUPTED
;
226 buffer
= memchr(buffer
, '>', buffer_end
- buffer
);
228 return GIT_EOBJCORRUPTED
;
230 commit
->time
= strtol((char *)buffer
+ 2, NULL
, 10);
231 if (commit
->time
== 0)
232 return GIT_EOBJCORRUPTED
;
238 static int commit_parse(git_revwalk
*walk
, commit_object
*commit
)
246 if ((error
= git_odb_read(&obj
, walk
->repo
->db
, &commit
->oid
)) < GIT_SUCCESS
)
249 if (obj
->raw
.type
!= GIT_OBJ_COMMIT
) {
250 git_odb_object_close(obj
);
254 error
= commit_quick_parse(walk
, commit
, &obj
->raw
);
255 git_odb_object_close(obj
);
259 static void mark_uninteresting(commit_object
*commit
)
264 commit
->uninteresting
= 1;
266 for (i
= 0; i
< commit
->out_degree
; ++i
)
267 if (!commit
->parents
[i
]->uninteresting
)
268 mark_uninteresting(commit
->parents
[i
]);
271 static int process_commit(git_revwalk
*walk
, commit_object
*commit
)
280 if ((error
= commit_parse(walk
, commit
)) < GIT_SUCCESS
)
283 if (commit
->uninteresting
)
284 mark_uninteresting(commit
);
286 return walk
->enqueue(walk
, commit
);
289 static int process_commit_parents(git_revwalk
*walk
, commit_object
*commit
)
292 int error
= GIT_SUCCESS
;
294 for (i
= 0; i
< commit
->out_degree
&& error
== GIT_SUCCESS
; ++i
) {
295 error
= process_commit(walk
, commit
->parents
[i
]);
301 static int push_commit(git_revwalk
*walk
, const git_oid
*oid
, int uninteresting
)
303 commit_object
*commit
;
305 commit
= commit_lookup(walk
, oid
);
307 return GIT_ENOTFOUND
;
309 commit
->uninteresting
= uninteresting
;
311 return process_commit(walk
, commit
);
314 int git_revwalk_push(git_revwalk
*walk
, const git_oid
*oid
)
317 return push_commit(walk
, oid
, 0);
320 int git_revwalk_hide(git_revwalk
*walk
, const git_oid
*oid
)
323 return push_commit(walk
, oid
, 1);
326 static int revwalk_enqueue_timesort(git_revwalk
*walk
, commit_object
*commit
)
328 return git_pqueue_insert(&walk
->iterator_time
, commit
);
331 static int revwalk_enqueue_unsorted(git_revwalk
*walk
, commit_object
*commit
)
333 return commit_list_insert(commit
, &walk
->iterator_rand
) ? GIT_SUCCESS
: GIT_ENOMEM
;
336 static int revwalk_next_timesort(commit_object
**object_out
, git_revwalk
*walk
)
341 while ((next
= git_pqueue_pop(&walk
->iterator_time
)) != NULL
) {
342 if ((error
= process_commit_parents(walk
, next
)) < GIT_SUCCESS
)
345 if (!next
->uninteresting
) {
351 return GIT_EREVWALKOVER
;
354 static int revwalk_next_unsorted(commit_object
**object_out
, git_revwalk
*walk
)
359 while ((next
= commit_list_pop(&walk
->iterator_rand
)) != NULL
) {
360 if ((error
= process_commit_parents(walk
, next
)) < GIT_SUCCESS
)
363 if (!next
->uninteresting
) {
369 return GIT_EREVWALKOVER
;
372 static int revwalk_next_toposort(commit_object
**object_out
, git_revwalk
*walk
)
378 next
= commit_list_pop(&walk
->iterator_topo
);
380 return GIT_EREVWALKOVER
;
382 if (next
->in_degree
> 0) {
383 next
->topo_delay
= 1;
387 for (i
= 0; i
< next
->out_degree
; ++i
) {
388 commit_object
*parent
= next
->parents
[i
];
390 if (--parent
->in_degree
== 0 && parent
->topo_delay
) {
391 parent
->topo_delay
= 0;
392 commit_list_insert(parent
, &walk
->iterator_topo
);
401 static int revwalk_next_reverse(commit_object
**object_out
, git_revwalk
*walk
)
403 *object_out
= commit_list_pop(&walk
->iterator_reverse
);
404 return *object_out
? GIT_SUCCESS
: GIT_EREVWALKOVER
;
408 static int prepare_walk(git_revwalk
*walk
)
413 if (walk
->sorting
& GIT_SORT_TOPOLOGICAL
) {
416 while ((error
= walk
->get_next(&next
, walk
)) == GIT_SUCCESS
) {
417 for (i
= 0; i
< next
->out_degree
; ++i
) {
418 commit_object
*parent
= next
->parents
[i
];
422 commit_list_insert(next
, &walk
->iterator_topo
);
425 if (error
!= GIT_EREVWALKOVER
)
428 walk
->get_next
= &revwalk_next_toposort
;
431 if (walk
->sorting
& GIT_SORT_REVERSE
) {
433 while ((error
= walk
->get_next(&next
, walk
)) == GIT_SUCCESS
)
434 commit_list_insert(next
, &walk
->iterator_reverse
);
436 if (error
!= GIT_EREVWALKOVER
)
439 walk
->get_next
= &revwalk_next_reverse
;
450 int git_revwalk_new(git_revwalk
**revwalk_out
, git_repository
*repo
)
454 walk
= git__malloc(sizeof(git_revwalk
));
458 memset(walk
, 0x0, sizeof(git_revwalk
));
460 walk
->commits
= git_hashtable_alloc(64,
462 (git_hash_keyeq_ptr
)git_oid_cmp
);
464 if (walk
->commits
== NULL
) {
469 git_pqueue_init(&walk
->iterator_time
, 8, commit_time_cmp
);
470 git_vector_init(&walk
->memory_alloc
, 8, NULL
);
473 walk
->get_next
= &revwalk_next_unsorted
;
474 walk
->enqueue
= &revwalk_enqueue_unsorted
;
482 void git_revwalk_free(git_revwalk
*walk
)
489 git_revwalk_reset(walk
);
490 git_hashtable_free(walk
->commits
);
491 git_pqueue_free(&walk
->iterator_time
);
493 for (i
= 0; i
< walk
->memory_alloc
.length
; ++i
)
494 free(git_vector_get(&walk
->memory_alloc
, i
));
496 git_vector_free(&walk
->memory_alloc
);
500 git_repository
*git_revwalk_repository(git_revwalk
*walk
)
506 void git_revwalk_sorting(git_revwalk
*walk
, unsigned int sort_mode
)
511 git_revwalk_reset(walk
);
513 walk
->sorting
= sort_mode
;
515 if (walk
->sorting
& GIT_SORT_TIME
) {
516 walk
->get_next
= &revwalk_next_timesort
;
517 walk
->enqueue
= &revwalk_enqueue_timesort
;
519 walk
->get_next
= &revwalk_next_unsorted
;
520 walk
->enqueue
= &revwalk_enqueue_unsorted
;
524 int git_revwalk_next(git_oid
*oid
, git_revwalk
*walk
)
531 if (!walk
->walking
) {
532 if ((error
= prepare_walk(walk
)) < GIT_SUCCESS
)
536 error
= walk
->get_next(&next
, walk
);
537 if (error
< GIT_SUCCESS
) {
538 if (error
== GIT_EREVWALKOVER
)
539 git_revwalk_reset(walk
);
543 git_oid_cpy(oid
, &next
->oid
);
547 void git_revwalk_reset(git_revwalk
*walk
)
550 commit_object
*commit
;
554 GIT_HASHTABLE_FOREACH(walk
->commits
, _unused
, commit
,
556 commit
->in_degree
= 0;
557 commit
->topo_delay
= 0;
560 git_pqueue_clear(&walk
->iterator_time
);
561 commit_list_free(&walk
->iterator_topo
);
562 commit_list_free(&walk
->iterator_rand
);
563 commit_list_free(&walk
->iterator_reverse
);