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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
(** Copy commits to one file to another *)
open Utils
open Lwt.Syntax
open Result_lwt.Syntax
open Result_lwt.Infix
let report_gc () =
Stdlib.Gc.print_stat Stdlib.stderr
let copy vc1 cs vc2 =
Log.(set default);
let commits1 = Vc.commit_db vc1 in
let+=? (n_done, _, _) =
Result_lwt.fold_leftM (fun (n_done, _last_src, _last_dst) c1 ->
let* () =
if n_done mod 100 = 0 then
Lwt_fmt.eprintf "%d vc1: %a vc2: %a@."
n_done Context.pp_cache_for_debug (Vc.context vc1) Context.pp_cache_for_debug (Vc.context vc2)
else Lwt.return_unit
in
let* () =
if n_done mod 1000 = 0 then begin
let* () = Lwt_fmt.eprintf "%d reopen vc1@." n_done in
let* (), sec = with_time_lwt @@ fun () ->
Storage.reopen (Vc.context vc1).Context.storage in
let* () = Lwt_fmt.eprintf "%d reopen vc1 in %a@." n_done Mtime.Span.pp sec in
let* () = Lwt_fmt.eprintf "%d reopen vc2@." n_done in
let* (), sec = with_time_lwt @@ fun () ->
Storage.reopen (Vc.context vc2).Context.storage in
Lwt_fmt.eprintf "%d reopen vc2 in %a@." n_done Mtime.Span.pp sec
end else Lwt.return_unit
in
let* () =
if n_done mod 1000 = 0 then begin
Stdlib.Gc.compact ();
let (), sec = with_time @@ report_gc in
Lwt_fmt.eprintf "%d gc: %a@." n_done Mtime.Span.pp sec
end else Lwt.return_unit
in
let* src =
Lwt.map from_Some @@ Vc.checkout ~keep_info:true vc1 c1.Commit.hash
in
let*=? (parent, (psrc, pdst)) =
match Commit_db.parent commits1 c1 with
| Ok None | Error `Not_found ->
Result_lwt.return (None, (Vc.empty vc1, Vc.empty vc2))
| Ok (Some p1) ->
let+ p =
let* res = Vc.checkout' vc2 p1.Commit.hash in
match res with
| None -> Lwt.return (Vc.empty vc1, Vc.empty vc2)
| Some (_p2, cur2) ->
let+ pcur = Lwt.map from_Some @@ Vc.checkout vc1 p1.Commit.hash in
(pcur, cur2)
in
Ok (Some p1.Commit.hash, p)
in
let _, nhpsrc = Cursor.compute_hash psrc in
let _, nhpdst = Cursor.compute_hash pdst in
assert (nhpsrc = nhpdst);
let Cursor.Cursor (tr, pn, ctxt1, _) = psrc in
let Cursor.Cursor (tr', n, ctxt1', info) = src in
let Cursor(_, _, dctxt, _) = pdst in
let copies = info.Info.copies in
if List.length copies <> 0 then
Format.eprintf "%d COPIES %d@." n_done (List.length copies);
let tbl = Hashtbl.create 1023 in
let visit c segs =
Deep.deep ~go_up:false ~create_subtrees:false
c
segs
(fun c seg ->
Cursor.access_gen c seg >>? function
| Reached (c, _) ->
let node n =
let n, h = Node_hash.compute dctxt.hash (Node_storage.read_hash dctxt) n in
let v = Node_storage.view dctxt n in
if Hashtbl.mem tbl h then `Return (n, ())
else begin
let i = from_Some @@ Node_type.index n in
Hashtbl.add tbl h i;
`Continue v
end
in
let view = Node_type.Mapper.default_mkview in
let Cursor.Cursor (_, n, _, _) = c in
let n, () = Node_type.Mapper.map ~node ~view n in
let Cursor.Cursor (trail, _, ctxt, info) = c in
Ok (Cursor._Cursor (trail, n, ctxt, info), ())
| HashOnly _ as e -> Cursor.error_access e
| _res ->
Ok (c, ())
) >|? fun (c, ()) ->
(Cursor.go_top c, ())
in
let (pdst, ()), sec =
with_time @@ fun () ->
from_Ok @@ Result.fold_leftM (fun (c,()) path ->
match visit c (Path.to_segments path) with
| Ok res -> Ok res
| Error _e ->
Ok (c,())) (pdst,()) copies
in
let* () =
if List.length copies <> 0 then begin
let* () =
Lwt_fmt.eprintf "%d COPIES %d HASHES %d visited in %a@."
n_done
(List.length copies)
(Hashtbl.length tbl)
Mtime.Span.pp sec
in
let (), sec = with_time @@ fun () ->
Hashtbl.iter (fun h i ->
Node_cache.add dctxt.node_cache h i ) tbl;
in
let* () =
Lwt_fmt.eprintf "%d COPIES %d HASHES %d added in %a@."
n_done
(List.length copies)
(Hashtbl.length tbl)
Mtime.Span.pp sec
in
Lwt_fmt.eprintf "%d vc1: %a vc2: %a@."
n_done Context.pp_cache_for_debug (Vc.context vc1) Context.pp_cache_for_debug (Vc.context vc2)
end else Lwt.return_unit
in
assert (tr = Cursor._Top && tr' = Cursor._Top);
assert (ctxt1 == ctxt1');
let diffs, sec = with_time @@ fun () -> Diff.diff ctxt1 pn n in
let* () = Lwt_fmt.eprintf "%d DIFFS %d in %a@." n_done (List.length diffs) Mtime.Span.pp sec in
let* res =
with_time_lwt @@ fun () ->
Result_lwt.fold_leftM (fun c diff ->
let diff, sec = with_time @@ fun () -> Diff.reset_for_another_context' ~src:ctxt1 ~dst:dctxt diff in
let+ () =
if Mtime.Span.to_s sec > 1.0 then
Lwt_fmt.eprintf "%d DIFF reset took long time: %a@." n_done Mtime.Span.pp sec
else Lwt.return_unit
in
Diff.apply c diff) pdst diffs
in
match res with
| Error e, _ -> Lwt.return (Error e)
| Ok cur2, sec ->
let* () =
Lwt_fmt.eprintf "%d DIFFS %d applied in %a@."
n_done (List.length diffs) Mtime.Span.pp sec
in
let*=? cur2, _hp, _commit =
Vc.commit
~allow_missing_parent: true
vc2
~parent
~hash_override: (Some c1.Commit.hash)
cur2
in
let _, nhsrc = Cursor.compute_hash src in
let _, nhdst = Cursor.compute_hash cur2 in
if nhsrc <> nhdst then begin
Log.fatal "COPY IS BUGGY";
assert (nhsrc = nhdst);
end;
let Cursor.Cursor (_, srcn, _, _) = src in
let srci = from_Some @@ Node_type.index srcn in
let Cursor.Cursor (_, cur2n, _, _) = cur2 in
let cur2i = from_Some @@ Node_type.index cur2n in
Log.notice "%d IDXs %a %a" n_done Index.pp srci Index.pp cur2i;
Result_lwt.return (n_done + 1, src, cur2)
) (0, Vc.empty vc1, Vc.empty vc2) cs
in
n_done