Source file traverse_bfs.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
module SHA1 = struct
include Digestif.SHA1
module Set = Set.Make (struct
type nonrec t = t
let compare = unsafe_compare
end)
end
let src = Logs.Src.create "git.traverse" ~doc:"logs git's traverse event"
module Log = (val Logs.src_log src : Logs.LOG)
module type S = sig
type t
val read_exn : t -> SHA1.t -> Git_object.t
val root : t -> Fpath.t
val is_shallowed : t -> SHA1.t -> bool Lwt.t
end
module Make (Store : S) = struct
let fold
t
(fn :
'acc ->
?name:Fpath.t ->
length:int64 ->
SHA1.t ->
Git_object.t ->
'acc Lwt.t)
~path
acc
hash =
let names = Hashtbl.create 0x100 in
let open Lwt.Infix in
let rec walk close rest queue acc =
match rest with
| [] -> (
match Queue.pop queue with
| rest -> walk close [rest] queue acc
| exception Queue.Empty -> Lwt.return acc)
| hash :: rest -> (
if SHA1.Set.mem hash close then walk close rest queue acc
else
let close' = SHA1.Set.add hash close in
match Store.read_exn t hash with
| Git_object.Commit commit as value -> begin
let rest' = Git_commit.tree commit :: rest in
Store.is_shallowed t hash >>= function
| true ->
fn acc ~length:(Git_commit.length commit) hash value
>>= fun acc' -> walk close' rest' queue acc'
| false ->
List.iter (fun x -> Queue.add x queue) (Git_commit.parents commit);
fn acc ~length:(Git_commit.length commit) hash value
>>= fun acc' -> walk close' rest' queue acc'
end
| Git_object.Tree tree as value ->
let path = try Hashtbl.find names hash with Not_found -> path in
Lwt_list.iter_s
(fun {Git_tree.name; node; _} ->
Hashtbl.add names node Fpath.(path / name);
Lwt.return ())
(Git_tree.to_list tree)
>>= fun () ->
let rest' =
rest
@ List.map
(fun {Git_tree.node; _} -> node)
(Git_tree.to_list tree)
in
fn acc ~name:path ~length:(Git_tree.length tree) hash value
>>= fun acc' -> walk close' rest' queue acc'
| Git_object.Blob blob as value ->
let path = try Hashtbl.find names hash with Not_found -> path in
fn acc ~name:path ~length:(Git_blob.length blob) hash value
>>= fun acc' -> walk close' rest queue acc'
| Git_object.Tag tag as value ->
Queue.add (Git_tag.obj tag) queue;
fn acc ~length:(Git_tag.length tag) hash value >>= fun acc' ->
walk close' rest queue acc')
in
walk SHA1.Set.empty [hash] (Queue.create ()) acc
let iter t fn hash =
fold t
(fun () ?name:_ ~length:_ hash value -> fn hash value)
~path:(Store.root t) () hash
end