98286406 Andy McCall 1 month ago ↶
1
2
3
use super :: * ;
75167672 Andy McCall 1 month ago
4
use crate :: protocol:: SignedPolicy;
957fee30 Andy McCall 1 month ago ↶
5
use crate :: repo:: { BisectStep, Selection} ;
98286406 Andy McCall 1 month ago ↶
6
7
8
9
fn hermetic ( ) {
10
static ONCE : std:: sync:: Once = std:: sync:: Once:: new( ) ;
11
static DIR : std:: sync:: OnceLock< tempfile:: TempDir> = std:: sync:: OnceLock:: new( ) ;
12
ONCE .call_once ( | | {
13
let d = tempfile:: tempdir( ) .unwrap ( ) ;
14
std:: env:: set_var( " OXY_HOME" , d.path ( ) ) ;
15
DIR .set ( d) .ok ( ) ;
16
} ) ;
17
}
18
19
fn write ( root : & Path, rel : & str , content : & [u8 ]) {
20
let p = root.join ( rel) ;
21
fs:: create_dir_all( p.parent ( ) .unwrap ( ) ) .unwrap ( ) ;
22
fs:: write( p, content) .unwrap ( ) ;
23
}
24
25
fn statuses ( s : & SnapshotResult) -> Vec< ( String, ChangeStatus) > {
75167672 Andy McCall 1 month ago
26
s.statuses
27
.iter ( )
28
.map ( | p | ( p.path.clone ( ) , p.status) )
29
.collect ( )
98286406 Andy McCall 1 month ago ↶
30
}
31
32
# [ test ]
33
fn init_snapshot_commit_log_undo ( ) {
34
hermetic ( ) ;
35
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
36
let root = dir.path ( ) ;
37
let repo = Repo:: init( root) .unwrap ( ) ;
38
assert! ( Repo:: init( root) .is_err ( ) ) ;
39
let ops = repo.op_log ( ) .unwrap ( ) ;
40
assert_eq! ( ops.len ( ) , 1 ) ;
41
assert_eq! ( ops[ 0 ] .1. command, " oxy init" ) ;
42
43
44
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
45
assert! ( ! s.changed) ;
46
assert_eq! ( repo.op_log ( ) .unwrap ( ) .len ( ) , 1 ) ;
47
48
write ( root, " src/main.rs" , b " fn main() {}\n " ) ;
49
write ( root, " README.md" , b " # Hello\n " ) ;
50
write ( root, " .oxyignore" , b " /target\n " ) ;
51
write ( root, " target/junk.o" , b " \0 \0 \0 " ) ;
52
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
53
assert! ( s.changed) ;
54
assert_eq! (
55
statuses ( & s) ,
56
vec! [
57
( " .oxyignore" .into ( ) , ChangeStatus:: Added) ,
58
( " README.md" .into ( ) , ChangeStatus:: Added) ,
59
( " src/main.rs" .into ( ) , ChangeStatus:: Added) ,
60
]
61
) ;
62
assert_eq! ( repo.op_log ( ) .unwrap ( ) .len ( ) , 2 ) ;
63
64
let s2 = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
65
assert! ( ! s2.changed) ;
66
assert_eq! ( s2.wc_hash, s.wc_hash) ;
67
assert_eq! ( repo.op_log ( ) .unwrap ( ) .len ( ) , 2 ) ;
68
69
let h1 = repo.commit ( " Initial import" , & [ ] ) .unwrap ( ) ;
70
let c1 = repo.store ( ) .get_change ( & h1) .unwrap ( ) ;
71
assert_eq! ( c1.kind, ChangeKind:: Normal) ;
72
assert! ( c1.deps.is_empty ( ) , " first change has nothing to depend on" ) ;
73
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h1] ) ;
74
let ( wc_hash, wc) = repo.working_copy ( ) .unwrap ( ) ;
75
assert_eq! ( wc.deps, vec! [ h1] ) ;
76
assert! ( wc.edits.is_empty ( ) ) ;
77
assert! ( repo.commit ( " nothing" , & [ ] ) .is_err ( ) ) ;
78
79
75167672 Andy McCall 1 month ago
80
write (
81
root,
82
" src/main.rs" ,
83
b " fn main() {\n println!(\" hi\" );\n }\n " ,
84
) ;
98286406 Andy McCall 1 month ago ↶
85
fs:: remove_file( root.join ( " README.md" ) ) .unwrap ( ) ;
86
write ( root, " logo.png" , b " \x89 PNG\0 \0 \x01 " ) ;
87
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
88
assert_eq! (
89
statuses ( & s) ,
90
vec! [
91
( " README.md" .into ( ) , ChangeStatus:: Removed) ,
92
( " logo.png" .into ( ) , ChangeStatus:: Added) ,
93
( " src/main.rs" .into ( ) , ChangeStatus:: Modified) ,
94
]
95
) ;
96
assert_ne! ( s.wc_hash, wc_hash) ;
97
98
assert_eq! ( s.wc.change_id, wc.change_id) ;
99
assert! ( s.wc.deps.contains ( & h1) ) ;
100
101
let h2 = repo.commit ( " Second" , & [ ] ) .unwrap ( ) ;
102
let c2 = repo.store ( ) .get_change ( & h2) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
103
assert_eq! (
104
c2.deps,
105
vec! [ h1] ,
106
" everything it touches was introduced by h1"
107
) ;
98286406 Andy McCall 1 month ago ↶
108
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2] ) ;
109
110
111
let st = repo.load_state ( & [ h2] ) .unwrap ( ) ;
112
let snap = st.materialize ( ) ;
113
assert! ( ! snap.is_conflicted ( ) ) ;
75167672 Andy McCall 1 month ago
114
assert_eq! (
115
snap.files.keys ( ) .cloned ( ) .collect:: < Vec< _ > > ( ) ,
116
[ " .oxyignore" , " logo.png" , " src" , " src/main.rs" ]
117
) ;
118
assert! (
119
matches! ( & snap.files[ " src/main.rs" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " fn main() {\n println!(\" hi\" );\n }\n " )
120
) ;
98286406 Andy McCall 1 month ago ↶
121
assert! ( snap.files[ " logo.png" ] .mode.opaque) ;
122
if let FileContent:: Opaque { blobs } = & snap.files[ " logo.png" ] .content {
123
assert_eq! ( repo.read_blob ( & blobs[ 0 ] ) .unwrap ( ) , b " \x89 PNG\0 \0 \x01 " ) ;
124
} else {
125
panic! ( " logo should be opaque" ) ;
126
}
127
128
129
let log = repo.log ( ) .unwrap ( ) ;
130
assert_eq! ( log.len ( ) , 3 ) ;
131
assert! ( log[ 0 ] .is_working_copy) ;
132
assert_eq! ( log[ 1 ] .hash, h2) ;
133
assert! ( log[ 1 ] .is_head) ;
134
assert_eq! ( log[ 2 ] .hash, h1) ;
135
let d = repo.describe_change ( & h2) .unwrap ( ) ;
136
assert! ( d.contains ( " D README.md" ) , " {d}" ) ;
137
assert! ( d.contains ( " + println!(\" hi\" );" ) , " {d}" ) ;
138
assert! ( d.contains ( " -fn main() {}" ) , " {d}" ) ;
139
140
141
assert_eq! ( repo.resolve_change ( & c2.change_id.short ( 6 ) ) .unwrap ( ) , h2) ;
75167672 Andy McCall 1 month ago
142
assert_eq! (
143
repo.resolve_change ( & format! ( " #{} " , h1.short ( 8 ) ) ) .unwrap ( ) ,
144
h1
145
) ;
98286406 Andy McCall 1 month ago ↶
146
assert! ( repo.resolve_change ( " zzzz" ) .is_err ( ) ) ;
147
148
149
repo.undo ( ) .unwrap ( ) ;
150
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h1] ) ;
151
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
152
assert! (
153
! s.changed,
154
" files on disk still match the pre-commit working copy"
155
) ;
98286406 Andy McCall 1 month ago ↶
156
assert_eq! ( s.statuses.len ( ) , 3 ) ;
157
}
158
159
160
fn conflicted_repo ( ) -> ( tempfile:: TempDir, Repo, ChangeHash, ChangeHash, ChangeHash) {
161
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
162
let root = dir.path ( ) ;
163
let repo = Repo:: init( root) .unwrap ( ) ;
164
write ( root, " f.txt" , b " a\n b\n c\n " ) ;
165
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
166
write ( root, " f.txt" , b " a\n B\n c\n " ) ;
167
let h2 = repo.commit ( " upper" , & [ ] ) .unwrap ( ) ;
168
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
169
write ( root, " f.txt" , b " a\n X\n c\n " ) ;
170
let h3 = repo.commit ( " ex" , & [ ] ) .unwrap ( ) ;
171
( dir, repo, h1, h2, h3)
172
}
173
3652104a Andy McCall 19 days ago ↶
174
175
176
98286406 Andy McCall 1 month ago ↶
177
# [ test ]
3652104a Andy McCall 19 days ago ↶
178
fn marker_like_text_does_not_block_a_resolution ( ) {
179
hermetic ( ) ;
180
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
181
let root = dir.path ( ) ;
182
let repo = Repo:: init( root) .unwrap ( ) ;
183
let example = " ```\n <<<<<<< oxy conflict 1 of 2, side 1: change k7m2p9 \" Add retry loop\" \n ... lines ...\n ======= oxy conflict 1 of 2, side 2: change q3x8w1 \" Refactor client\" \n >>>>>>> oxy conflict 1 of 2 end\n ```\n " ;
184
write (
185
root,
186
" doc.md" ,
187
format! ( " intro\n {example} b\n end\n " ) .as_bytes ( ) ,
188
) ;
189
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
190
write (
191
root,
192
" doc.md" ,
193
format! ( " intro\n {example} B\n end\n " ) .as_bytes ( ) ,
194
) ;
195
repo.commit ( " upper" , & [ ] ) .unwrap ( ) ;
196
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
197
write (
198
root,
199
" doc.md" ,
200
format! ( " intro\n {example} X\n end\n " ) .as_bytes ( ) ,
201
) ;
202
repo.commit ( " ex" , & [ ] ) .unwrap ( ) ;
203
repo.checkout ( & repo.bookmark_heads ( ) .unwrap ( ) , false , " checkout main" )
204
.unwrap ( ) ;
205
let marked = fs:: read_to_string( root.join ( " doc.md" ) ) .unwrap ( ) ;
206
assert! ( marked.contains ( " oxy conflict 1 of 1" ) , " {marked}" ) ;
207
208
write ( root, " doc.md" , marked.replace ( " B\n " , " BB\n " ) .as_bytes ( ) ) ;
209
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
210
assert_eq! (
211
statuses ( & s) ,
212
vec! [ ( " doc.md" .into ( ) , ChangeStatus:: ConflictEdited) ]
213
) ;
214
215
write (
216
root,
217
" doc.md" ,
218
format! ( " intro\n {example} X\n end\n " ) .as_bytes ( ) ,
219
) ;
220
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
221
assert_eq! (
222
statuses ( & s) ,
223
vec! [ ( " doc.md" .into ( ) , ChangeStatus:: Resolved) ]
224
) ;
225
let h = repo.commit ( " resolve" , & [ ] ) .unwrap ( ) ;
226
assert_eq! (
227
repo.store ( ) .get_change ( & h) .unwrap ( ) .kind,
228
ChangeKind:: Resolution
229
) ;
230
assert! ( ! repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) .is_conflicted ( ) ) ;
231
}
232
233
# [ test ]
98286406 Andy McCall 1 month ago ↶
234
fn concurrent_edits_conflict_and_can_be_resolved ( ) {
235
hermetic ( ) ;
236
let ( dir, repo, h1, h2, h3) = conflicted_repo ( ) ;
237
let root = dir.path ( ) ;
238
let heads: BTreeSet< _ > = repo.bookmark_heads ( ) .unwrap ( ) .into_iter ( ) .collect ( ) ;
239
assert_eq! ( heads, [ h2, h3] .into_iter ( ) .collect ( ) ) ;
240
let st = repo.load_state ( & [ h2, h3] ) .unwrap ( ) ;
241
assert! ( st.materialize ( ) .is_conflicted ( ) ) ;
242
243
75167672 Andy McCall 1 month ago
244
let r = repo
245
.checkout ( & repo.bookmark_heads ( ) .unwrap ( ) , false , " checkout main" )
246
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
247
assert! ( r.conflicted) ;
248
let marked = fs:: read_to_string( root.join ( " f.txt" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
249
assert! (
250
marked.starts_with ( " a\n <<<<<<< oxy conflict 1 of 1" ) ,
251
" {marked}"
252
) ;
253
assert! (
254
marked.contains ( " B\n " ) & & marked.contains ( " X\n " ) & & marked.ends_with ( " end\n c\n " ) ,
255
" {marked}"
256
) ;
98286406 Andy McCall 1 month ago ↶
257
assert! ( has_conflict_markers ( marked.as_bytes ( ) ) ) ;
258
259
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
260
assert! ( s.statuses.is_empty ( ) ) ;
261
assert! ( ! s.resolves) ;
262
263
264
write ( root, " f.txt" , marked.replace ( " B\n " , " BB\n " ) .as_bytes ( ) ) ;
265
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
266
assert_eq! (
267
statuses ( & s) ,
268
vec! [ ( " f.txt" .into ( ) , ChangeStatus:: ConflictEdited) ]
269
) ;
98286406 Andy McCall 1 month ago ↶
270
assert! ( repo.commit ( " no" , & [ ] ) .is_err ( ) ) ;
271
272
273
write ( root, " f.txt" , b " a\n X\n c\n d\n " ) ;
274
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
275
assert_eq! ( statuses ( & s) , vec! [ ( " f.txt" .into ( ) , ChangeStatus:: Resolved) ] ) ;
276
assert! ( s.resolves) ;
277
let h4 = repo.commit ( " resolve" , & [ ] ) .unwrap ( ) ;
278
let c4 = repo.store ( ) .get_change ( & h4) .unwrap ( ) ;
279
assert_eq! ( c4.kind, ChangeKind:: Resolution) ;
280
let mut want = vec! [ h1, h2, h3] ;
281
want.sort ( ) ;
75167672 Andy McCall 1 month ago
282
assert_eq! (
283
c4.deps, want,
284
" a resolution depends on exactly the contributors and what it references"
285
) ;
98286406 Andy McCall 1 month ago ↶
286
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h4] ) ;
287
let snap = repo.load_state ( & [ h4] ) .unwrap ( ) .materialize ( ) ;
288
assert! ( ! snap.is_conflicted ( ) ) ;
75167672 Andy McCall 1 month ago
289
assert! (
290
matches! ( & snap.files[ " f.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " a\n X\n c\n d\n " )
291
) ;
98286406 Andy McCall 1 month ago ↶
292
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
293
294
295
let mut st = State:: new( ) ;
296
for h in [ h4, h3, h1, h2] {
297
st.add_unchecked ( repo.store ( ) .get_change ( & h) .unwrap ( ) ) ;
298
}
299
st.validate ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
300
assert! (
301
matches! ( & st.materialize ( ) .files[ " f.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " a\n X\n c\n d\n " )
302
) ;
98286406 Andy McCall 1 month ago ↶
303
}
304
305
# [ test ]
306
fn resolution_may_keep_both_sides_or_neither ( ) {
307
hermetic ( ) ;
308
let ( dir, repo, _h1, _h2, _h3) = conflicted_repo ( ) ;
309
let root = dir.path ( ) ;
75167672 Andy McCall 1 month ago
310
repo.checkout ( & repo.bookmark_heads ( ) .unwrap ( ) , false , " checkout main" )
311
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
312
313
write ( root, " f.txt" , b " a\n X\n B\n c\n " ) ;
314
let h = repo.commit ( " both" , & [ ] ) .unwrap ( ) ;
315
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
316
assert! ( ! snap.is_conflicted ( ) ) ;
75167672 Andy McCall 1 month ago
317
assert! (
318
matches! ( & snap.files[ " f.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " a\n X\n B\n c\n " )
319
) ;
98286406 Andy McCall 1 month ago ↶
320
321
repo.undo ( ) .unwrap ( ) ;
322
write ( root, " f.txt" , b " a\n c\n " ) ;
323
let h = repo.commit ( " neither" , & [ ] ) .unwrap ( ) ;
324
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
325
assert! ( ! snap.is_conflicted ( ) ) ;
75167672 Andy McCall 1 month ago
326
assert! (
327
matches! ( & snap.files[ " f.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " a\n c\n " )
328
) ;
98286406 Andy McCall 1 month ago ↶
329
}
330
331
# [ test ]
332
fn name_conflict_resolved_by_removing_a_candidate ( ) {
333
hermetic ( ) ;
334
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
335
let root = dir.path ( ) ;
336
let repo = Repo:: init( root) .unwrap ( ) ;
337
write ( root, " keep.txt" , b " k\n " ) ;
338
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
339
write ( root, " new.txt" , b " one\n " ) ;
340
repo.commit ( " one" , & [ ] ) .unwrap ( ) ;
341
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
342
write ( root, " new.txt" , b " two\n " ) ;
343
repo.commit ( " two" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
344
let r = repo
345
.checkout ( & repo.bookmark_heads ( ) .unwrap ( ) , false , " checkout main" )
346
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
347
assert! ( r.conflicted) ;
75167672 Andy McCall 1 month ago
348
assert! (
349
root.join ( " new.txt.oxy-conflict-1" ) .exists ( )
350
& & root.join ( " new.txt.oxy-conflict-2" ) .exists ( )
351
) ;
98286406 Andy McCall 1 month ago ↶
352
assert! ( ! root.join ( " new.txt" ) .exists ( ) ) ;
353
354
75167672 Andy McCall 1 month ago
355
let ( keep, drop) = if fs:: read( root.join ( " new.txt.oxy-conflict-1" ) ) .unwrap ( ) = = b " two\n " {
356
( 1 , 2 )
357
} else {
358
( 2 , 1 )
359
} ;
98286406 Andy McCall 1 month ago ↶
360
fs:: remove_file( root.join ( format! ( " new.txt.oxy-conflict-{drop} " ) ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
361
fs:: rename(
362
root.join ( format! ( " new.txt.oxy-conflict-{keep} " ) ) ,
363
root.join ( " new.txt" ) ,
364
)
365
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
366
let h = repo.commit ( " pick two" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
367
assert_eq! (
368
repo.store ( ) .get_change ( & h) .unwrap ( ) .kind,
369
ChangeKind:: Resolution
370
) ;
98286406 Andy McCall 1 month ago ↶
371
372
373
let heads: BTreeSet< _ > = repo.bookmark_heads ( ) .unwrap ( ) .into_iter ( ) .collect ( ) ;
374
assert_eq! ( heads, [ h1, h] .into_iter ( ) .collect ( ) ) ;
75167672 Andy McCall 1 month ago
375
let snap = repo
376
.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) )
377
.unwrap ( )
378
.materialize ( ) ;
98286406 Andy McCall 1 month ago ↶
379
assert! ( ! snap.is_conflicted ( ) , " {:?}" , snap.tree.conflicts) ;
75167672 Andy McCall 1 month ago
380
assert_eq! (
381
snap.files.keys ( ) .cloned ( ) .collect:: < Vec< _ > > ( ) ,
382
[ " keep.txt" , " new.txt" ]
383
) ;
384
assert! (
385
matches! ( & snap.files[ " new.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " two\n " )
386
) ;
98286406 Andy McCall 1 month ago ↶
387
}
388
389
# [ test ]
390
fn identities_and_signed_commits ( ) {
391
hermetic ( ) ;
392
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
393
let root = dir.path ( ) ;
394
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
395
write ( root, " a.txt" , b " a\n " ) ;
396
let h0 = repo.commit ( " unsigned" , & [ ] ) .unwrap ( ) ;
397
assert_eq! ( repo.verify_change ( & h0) .unwrap ( ) , SignatureStatus:: Unsigned) ;
75167672 Andy McCall 1 month ago
398
assert_eq! (
399
repo.store ( ) .get_change ( & h0) .unwrap ( ) .author,
400
ObjectRef:: ZERO
401
) ;
98286406 Andy McCall 1 month ago ↶
402
75167672 Andy McCall 1 month ago
403
let ( key, id) = repo
404
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: Repo)
405
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
406
assert_eq! ( id.hash ( ) , key) ;
407
assert_eq! ( repo.config.author_key, Some ( key) ) ;
408
409
let reopened = Repo:: open( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
410
assert_eq! ( reopened.config.author_key, Some ( key) ) ;
411
assert_eq! ( reopened.secret_key ( ) .unwrap ( ) .unwrap ( ) .0 , key) ;
412
413
414
write ( root, " a.txt" , b " a\n b\n " ) ;
415
let h1 = repo.commit ( " authored" , & [ ] ) .unwrap ( ) ;
416
assert_eq! ( repo.store ( ) .get_change ( & h1) .unwrap ( ) .author, key) ;
417
assert_eq! ( repo.verify_change ( & h1) .unwrap ( ) , SignatureStatus:: Unsigned) ;
418
419
repo.sign_change ( & h1) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
420
assert! (
421
matches! ( repo.verify_change ( & h1) .unwrap ( ) , SignatureStatus:: Valid { key: k, .. } if k = = key)
422
) ;
98286406 Andy McCall 1 month ago ↶
423
424
repo.config.set ( " core.sign" , " change" ) .unwrap ( ) ;
425
repo.save_config ( ) .unwrap ( ) ;
426
write ( root, " a.txt" , b " a\n b\n c\n " ) ;
427
let h2 = repo.commit ( " signed" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
428
assert_eq! (
429
repo.verify_change ( & h2) .unwrap ( ) ,
430
SignatureStatus:: Valid {
431
key,
432
name: " Ada" .into ( ) ,
433
email: " [email protected] " .into ( ) ,
434
sealed: false ,
435
revoked: false
436
}
437
) ;
98286406 Andy McCall 1 month ago ↶
438
439
440
let other = SecretKey:: generate( ) ;
441
let other_id = other.identity ( " Mallory" , " [email protected] " , 0 ) ;
442
let other_key = repo.store ( ) .put_identity ( & other_id) .unwrap ( ) ;
443
let forged = other.sign ( Domain:: Change, & other_key, & h0) ;
444
repo.store ( ) .put_signature ( & h0, & forged) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
445
assert! (
446
matches! ( repo.verify_change ( & h0) .unwrap ( ) , SignatureStatus:: Invalid( m) if m.contains ( " neither the author" ) )
447
) ;
98286406 Andy McCall 1 month ago ↶
448
449
450
let mut sig = repo.store ( ) .signatures ( & h2) .unwrap ( ) .remove ( 0 ) ;
451
sig.ed[ 0 ] ^ = 1 ;
452
repo.store ( ) .put_signature ( & h2, & sig) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
453
assert! ( matches! (
454
repo.verify_change ( & h2) .unwrap ( ) ,
455
SignatureStatus:: Invalid( _ )
456
) ) ;
98286406 Andy McCall 1 month ago ↶
457
458
459
let no_keys = Repo:: open( root) .unwrap ( ) .with_home ( root.join ( " nowhere" ) ) ;
460
write ( root, " a.txt" , b " z\n " ) ;
461
assert! ( no_keys.commit ( " cannot sign" , & [ ] ) .is_err ( ) ) ;
462
}
463
464
# [ test ]
465
fn checkout_moves_the_working_copy ( ) {
466
hermetic ( ) ;
467
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
468
let root = dir.path ( ) ;
469
let repo = Repo:: init( root) .unwrap ( ) ;
470
write ( root, " a.txt" , b " one\n " ) ;
471
write ( root, " sub/deep/b.txt" , b " b\n " ) ;
472
let h1 = repo.commit ( " first" , & [ ] ) .unwrap ( ) ;
473
write ( root, " a.txt" , b " two\n " ) ;
474
fs:: remove_file( root.join ( " sub/deep/b.txt" ) ) .unwrap ( ) ;
475
write ( root, " bin.dat" , & [ 0 , 1 , 2 , 3 ] ) ;
476
let h2 = repo.commit ( " second" , & [ ] ) .unwrap ( ) ;
477
478
let r = repo.checkout ( & [ h1] , false , " oxy checkout" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
479
assert_eq! (
480
r,
481
CheckoutReport {
482
written: 2 ,
483
removed: 1 ,
484
conflicted: false ,
485
notes: vec! [ ]
486
}
487
) ;
98286406 Andy McCall 1 month ago ↶
488
assert_eq! ( fs:: read( root.join ( " a.txt" ) ) .unwrap ( ) , b " one\n " ) ;
489
assert_eq! ( fs:: read( root.join ( " sub/deep/b.txt" ) ) .unwrap ( ) , b " b\n " ) ;
490
assert! ( ! root.join ( " bin.dat" ) .exists ( ) ) ;
491
let ( _ , wc) = repo.working_copy ( ) .unwrap ( ) ;
492
assert_eq! ( wc.deps, vec! [ h1] ) ;
75167672 Andy McCall 1 month ago
493
assert! (
494
! repo.snapshot ( " status" ) .unwrap ( ) .changed,
495
" checkout leaves a clean working copy"
496
) ;
98286406 Andy McCall 1 month ago ↶
497
498
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2] ) ;
499
500
501
write ( root, " a.txt" , b " dirty\n " ) ;
502
assert! ( repo.checkout ( & [ h2] , false , " oxy checkout" ) .is_err ( ) ) ;
75167672 Andy McCall 1 month ago
503
let r = repo
504
.checkout ( & [ h2] , true , " oxy checkout --discard" )
505
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
506
assert_eq! ( fs:: read( root.join ( " a.txt" ) ) .unwrap ( ) , b " two\n " ) ;
507
assert_eq! ( fs:: read( root.join ( " bin.dat" ) ) .unwrap ( ) , [ 0 , 1 , 2 , 3 ] ) ;
508
assert! ( ! root.join ( " sub" ) .exists ( ) , " empty directories are pruned" ) ;
509
assert_eq! ( r.removed, 1 ) ;
510
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
511
512
513
# [ cfg ( unix ) ]
514
{
515
fs:: remove_dir_all( root.join ( " sub" ) ) .ok ( ) ;
516
std:: os:: unix:: fs:: symlink( " /tmp" , root.join ( " sub" ) ) .unwrap ( ) ;
517
assert! ( repo.checkout ( & [ h1] , true , " oxy checkout" ) .is_err ( ) ) ;
518
}
519
}
520
521
# [ test ]
522
fn large_files_are_chunked_and_read_back ( ) {
523
hermetic ( ) ;
524
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
525
let repo = Repo:: init( dir.path ( ) ) .unwrap ( ) ;
526
let mut big = vec! [ 0 u8 ; 700 * 1024 ] ;
527
for ( i, b) in big.iter_mut ( ) .enumerate ( ) {
528
* b = ( i as u32 ) .wrapping_mul ( 2654435761 ) .to_le_bytes ( ) [ 0 ] ;
529
}
530
let r = repo.store_blob ( & big) .unwrap ( ) ;
531
let blob = Blob:: decode( & repo.store ( ) .get_typed ( & r, ObjectType:: Blob) .unwrap ( ) ) .unwrap ( ) ;
532
assert! ( blob.chunks.len ( ) > = 3 , " {}" , blob.chunks.len ( ) ) ;
533
assert_eq! ( repo.read_blob ( & r) .unwrap ( ) , big) ;
534
assert_eq! ( Repo:: blob_ref( & big) , r) ;
535
}
536
537
# [ test ]
538
fn clone_push_pull_with_seals_and_attestations ( ) {
539
hermetic ( ) ;
540
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
541
let root = dir.path ( ) ;
542
let origin_path = root.join ( " origin" ) ;
543
let origin = Repo:: init( & origin_path) .unwrap ( ) ;
544
let origin_url = origin_path.to_string_lossy ( ) .to_string ( ) ;
545
546
75167672 Andy McCall 1 month ago
547
let mut a = Repo:: clone( & origin_url, & root.join ( " a" ) )
548
.unwrap ( )
549
.with_home ( root.join ( " home-a" ) ) ;
98286406 Andy McCall 1 month ago ↶
550
assert_eq! ( a.id ( ) .unwrap ( ) , origin.id ( ) .unwrap ( ) ) ;
75167672 Andy McCall 1 month ago
551
let ( key_a, _ ) = a
552
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: Repo)
553
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
554
write ( & root.join ( " a" ) , " f.txt" , b " a\n " ) ;
555
let h1 = a.commit ( " first" , & [ ] ) .unwrap ( ) ;
556
let results = a.push ( " origin" , false ) .unwrap ( ) ;
557
assert! ( results[ 0 ] .ok, " {}" , results[ 0 ] .reason) ;
558
assert_eq! ( origin.bookmark_heads ( ) .unwrap ( ) , vec! [ h1] ) ;
559
75167672 Andy McCall 1 month ago
560
assert! (
561
matches! ( origin.authenticate_change ( & h1) .unwrap ( ) , SignatureStatus:: Valid { key, sealed: true , .. } if key = = key_a)
562
) ;
563
assert_eq! (
564
origin.verify_change ( & h1) .unwrap ( ) ,
565
SignatureStatus:: Unsigned
566
) ;
98286406 Andy McCall 1 month ago ↶
567
let ( _ , att) = origin.latest_ref_attest ( " main" ) .unwrap ( ) .unwrap ( ) ;
568
assert! ( matches! ( att.attest.body, AttestBody:: Ref { serial: 1 , .. } ) ) ;
569
let again = a.push ( " origin" , false ) .unwrap ( ) ;
570
assert! ( again[ 0 ] .ok & & again[ 0 ] .reason.contains ( " up to date" ) ) ;
571
let ( _ , att) = origin.latest_ref_attest ( " main" ) .unwrap ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
572
assert! (
573
matches! ( att.attest.body, AttestBody:: Ref { serial: 1 , .. } ) ,
574
" a no-op push does not attest again"
575
) ;
98286406 Andy McCall 1 month ago ↶
576
577
578
let b = Repo:: clone( & origin_url, & root.join ( " b" ) ) .unwrap ( ) ;
579
assert_eq! ( fs:: read( root.join ( " b/f.txt" ) ) .unwrap ( ) , b " a\n " ) ;
580
assert_eq! ( b.bookmark_heads ( ) .unwrap ( ) , vec! [ h1] ) ;
75167672 Andy McCall 1 month ago
581
assert! ( matches! (
582
b.authenticate_change ( & h1) .unwrap ( ) ,
583
SignatureStatus:: Valid { sealed: true , .. }
584
) ) ;
98286406 Andy McCall 1 month ago ↶
585
586
587
write ( & root.join ( " b" ) , " f.txt" , b " a\n b\n " ) ;
588
let h2 = b.commit ( " second" , & [ ] ) .unwrap ( ) ;
589
assert! ( b.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
590
assert_eq! ( origin.bookmark_heads ( ) .unwrap ( ) , vec! [ h2] ) ;
591
592
593
write ( & root.join ( " a" ) , " g.txt" , b " g\n " ) ;
594
let h3 = a.commit ( " third" , & [ ] ) .unwrap ( ) ;
595
assert! ( a.push ( " origin" , false ) .is_err ( ) ) ;
596
let pr = a.pull ( " origin" ) .unwrap ( ) ;
597
assert! ( pr.changed & & pr.checked_out) ;
598
let mut want = vec! [ h2, h3] ;
599
want.sort ( ) ;
600
assert_eq! ( pr.heads, want) ;
601
assert_eq! ( fs:: read( root.join ( " a/f.txt" ) ) .unwrap ( ) , b " a\n b\n " ) ;
602
assert_eq! ( fs:: read( root.join ( " a/g.txt" ) ) .unwrap ( ) , b " g\n " ) ;
603
let results = a.push ( " origin" , false ) .unwrap ( ) ;
604
assert! ( results[ 0 ] .ok, " {}" , results[ 0 ] .reason) ;
605
let mut origin_heads = origin.bookmark_heads ( ) .unwrap ( ) ;
606
origin_heads.sort ( ) ;
607
assert_eq! ( origin_heads, want) ;
608
let ( _ , att) = origin.latest_ref_attest ( " main" ) .unwrap ( ) .unwrap ( ) ;
609
assert! ( matches! ( att.attest.body, AttestBody:: Ref { serial: 2 , .. } ) ) ;
610
611
612
let pr = b.pull ( " origin" ) .unwrap ( ) ;
613
assert_eq! ( pr.heads, want) ;
614
assert_eq! ( fs:: read( root.join ( " b/g.txt" ) ) .unwrap ( ) , b " g\n " ) ;
615
assert! ( ! b.snapshot ( " status" ) .unwrap ( ) .changed) ;
616
617
618
write ( & root.join ( " a" ) , " f.txt" , b " a\n A\n " ) ;
619
a.commit ( " a-edit" , & [ ] ) .unwrap ( ) ;
620
a.push ( " origin" , false ) .unwrap ( ) ;
621
write ( & root.join ( " b" ) , " f.txt" , b " a\n B\n " ) ;
622
b.commit ( " b-edit" , & [ ] ) .unwrap ( ) ;
623
let pr = b.pull ( " origin" ) .unwrap ( ) ;
624
assert! ( pr.changed & & pr.checked_out) ;
625
let text = fs:: read_to_string( root.join ( " b/f.txt" ) ) .unwrap ( ) ;
626
assert! ( text.contains ( " <<<<<<< oxy conflict" ) , " {text}" ) ;
627
write ( & root.join ( " b" ) , " f.txt" , b " a\n AB\n " ) ;
628
let hr = b.commit ( " resolve" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
629
assert_eq! (
630
b.store ( ) .get_change ( & hr) .unwrap ( ) .kind,
631
ChangeKind:: Resolution
632
) ;
98286406 Andy McCall 1 month ago ↶
633
assert! ( b.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
634
a.pull ( " origin" ) .unwrap ( ) ;
635
assert_eq! ( fs:: read( root.join ( " a/f.txt" ) ) .unwrap ( ) , b " a\n AB\n " ) ;
636
637
638
let mut want = vec! [ hr, h3] ;
639
want.sort ( ) ;
640
assert_eq! ( a.bookmark_heads ( ) .unwrap ( ) , want) ;
641
assert! ( ! a.load_state ( & want) .unwrap ( ) .materialize ( ) .is_conflicted ( ) ) ;
642
}
643
644
# [ test ]
645
fn receive_push_rejects_bad_updates ( ) {
646
hermetic ( ) ;
647
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
648
let root = dir.path ( ) ;
649
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
650
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
651
let mut a = Repo:: clone( & url, & root.join ( " a" ) )
652
.unwrap ( )
653
.with_home ( root.join ( " home" ) ) ;
654
let ( key, _ ) = a
655
.create_identity ( " Ada" , " a@b" , IdentityScope:: Repo)
656
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
657
write ( & root.join ( " a" ) , " f.txt" , b " x\n " ) ;
658
let h1 = a.commit ( " one" , & [ ] ) .unwrap ( ) ;
659
assert! ( a.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
660
let empty = origin.store ( ) .put_state ( & StateObject:: new( vec! [ ] ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
661
let s1 = origin
662
.store ( )
663
.put_state ( & StateObject:: new( vec! [ h1] ) )
664
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
665
666
75167672 Andy McCall 1 month ago
667
let ghost = origin
668
.store ( )
669
.put_state ( & StateObject:: new( vec! [ ObjectRef( [ 9 ; 32 ] ) ] ) )
670
.unwrap ( ) ;
671
let r = origin
672
.receive_push (
673
Bundle:: default( ) ,
674
vec! [ RefUpdate {
675
name: " main" .into ( ) ,
676
old_state: s1,
677
new_state: ghost,
678
force: false ,
679
attestation: None ,
680
} ] ,
681
None ,
682
vec! [ ] ,
683
)
684
.unwrap ( ) ;
685
assert! (
686
! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " missing" ) ,
687
" {}" ,
688
r[ 0 ] .reason
689
) ;
98286406 Andy McCall 1 month ago ↶
690
691
75167672 Andy McCall 1 month ago
692
let r = origin
693
.receive_push (
694
Bundle:: default( ) ,
695
vec! [ RefUpdate {
696
name: " main" .into ( ) ,
697
old_state: empty,
698
new_state: s1,
699
force: false ,
700
attestation: None ,
701
} ] ,
702
None ,
703
vec! [ ] ,
704
)
705
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
706
assert! ( ! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " stale" ) , " {}" , r[ 0 ] .reason) ;
707
708
75167672 Andy McCall 1 month ago
709
let r = origin
710
.receive_push (
711
Bundle:: default( ) ,
712
vec! [ RefUpdate {
713
name: " main" .into ( ) ,
714
old_state: s1,
715
new_state: empty,
716
force: false ,
717
attestation: None ,
718
} ] ,
719
None ,
720
vec! [ ] ,
721
)
722
.unwrap ( ) ;
723
assert! (
724
! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " fast-forward" ) ,
725
" {}" ,
726
r[ 0 ] .reason
727
) ;
728
let r = origin
729
.receive_push (
730
Bundle:: default( ) ,
731
vec! [ RefUpdate {
732
name: " main" .into ( ) ,
733
old_state: s1,
734
new_state: empty,
735
force: true ,
736
attestation: None ,
737
} ] ,
738
None ,
739
vec! [ ] ,
740
)
741
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
742
assert! ( r[ 0 ] .ok) ;
743
assert_eq! ( origin.bookmark_heads ( ) .unwrap ( ) , vec! [ ] ) ;
744
745
75167672 Andy McCall 1 month ago
746
let sa = a
747
.sign_attest ( & Attest {
748
repo: a.id ( ) .unwrap ( ) ,
749
hlc: Hlc:: now( ) ,
750
body: AttestBody:: Ref {
751
name: " main" .into ( ) ,
752
old_state: empty,
753
new_state: s1,
754
serial: 1 ,
755
} ,
756
} )
757
.unwrap ( ) ;
758
let r = origin
759
.receive_push (
760
Bundle:: default( ) ,
761
vec! [ RefUpdate {
762
name: " main" .into ( ) ,
763
old_state: empty,
764
new_state: s1,
765
force: false ,
766
attestation: Some ( sa.clone ( ) ) ,
767
} ] ,
768
None ,
769
vec! [ ] ,
770
)
771
.unwrap ( ) ;
772
assert! (
773
! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " serial" ) ,
774
" {}" ,
775
r[ 0 ] .reason
776
) ;
98286406 Andy McCall 1 month ago ↶
777
75167672 Andy McCall 1 month ago
778
let sa2 = a
779
.sign_attest ( & Attest {
780
repo: a.id ( ) .unwrap ( ) ,
781
hlc: Hlc:: now( ) ,
782
body: AttestBody:: Ref {
783
name: " main" .into ( ) ,
784
old_state: s1,
785
new_state: empty,
786
serial: 5 ,
787
} ,
788
} )
789
.unwrap ( ) ;
790
let r = origin
791
.receive_push (
792
Bundle:: default( ) ,
793
vec! [ RefUpdate {
794
name: " main" .into ( ) ,
795
old_state: empty,
796
new_state: s1,
797
force: false ,
798
attestation: Some ( sa2) ,
799
} ] ,
800
None ,
801
vec! [ ] ,
802
)
803
.unwrap ( ) ;
804
assert! (
805
! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " describe" ) ,
806
" {}" ,
807
r[ 0 ] .reason
808
) ;
98286406 Andy McCall 1 month ago ↶
809
let _ = key;
810
}
811
812
# [ test ]
813
fn user_config_supplies_defaults_and_clones_sign ( ) {
814
hermetic ( ) ;
815
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
816
let root = dir.path ( ) ;
817
let home = root.join ( " home" ) ;
818
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
819
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
820
821
822
let mut a = Repo:: init( & root.join ( " a" ) ) .unwrap ( ) .with_home ( & home) ;
75167672 Andy McCall 1 month ago
823
let ( key, _ ) = a
824
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
825
.unwrap ( ) ;
98286406 Andy McCall 1 month ago ↶
826
assert! ( ! a.config.repo_layer ( ) .contains_key ( " author.key" ) ) ;
827
assert_eq! ( a.config.author_key, Some ( key) ) ;
828
assert! ( home.join ( " config.toml" ) .exists ( ) ) ;
829
a.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
830
assert! ( a.config.sign_changes) ;
831
832
833
a.config.set ( " core.sign" , " seal" ) .unwrap ( ) ;
834
a.save_config ( ) .unwrap ( ) ;
835
let again = Repo:: open( & root.join ( " a" ) ) .unwrap ( ) .with_home ( & home) ;
836
assert! ( ! again.config.sign_changes) ;
837
assert_eq! ( again.config.author_key, Some ( key) ) ;
75167672 Andy McCall 1 month ago
838
assert_eq! (
839
read_config_file ( & root.join ( " a/.oxy/config" ) )
840
.unwrap ( )
841
.get ( " core.sign" )
842
.map ( String :: as_str) ,
843
Some ( " seal" )
844
) ;
98286406 Andy McCall 1 month ago ↶
845
846
847
848
let b = Repo:: clone( & url, & root.join ( " b" ) ) .unwrap ( ) .with_home ( & home) ;
849
assert! ( b.config.sign_changes) ;
850
write ( & root.join ( " b" ) , " f.txt" , b " f\n " ) ;
851
let h = b.commit ( " signed from a clone" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
852
assert! (
853
matches! ( b.verify_change ( & h) .unwrap ( ) , SignatureStatus:: Valid { key: k, .. } if k = = key)
854
) ;
98286406 Andy McCall 1 month ago ↶
855
assert! ( b.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
75167672 Andy McCall 1 month ago
856
assert! ( matches! (
857
origin.verify_change ( & h) .unwrap ( ) ,
858
SignatureStatus:: Valid { .. }
859
) ) ;
98286406 Andy McCall 1 month ago ↶
860
}
861
862
# [ test ]
863
fn format1_repositories_are_migrated_on_open ( ) {
864
hermetic ( ) ;
865
use crate :: oplog:: { Format1Op, Format1View} ;
866
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
867
let root = dir.path ( ) ;
868
let repo = Repo:: init( root) .unwrap ( ) ;
869
write ( root, " a.txt" , b " a\n " ) ;
870
let h1 = repo.commit ( " one" , & [ ] ) .unwrap ( ) ;
871
let ( wc_hash, _ ) = repo.working_copy ( ) .unwrap ( ) ;
872
873
let mut v1 = Format1View:: default( ) ;
874
v1.bookmarks.insert ( " main" .into ( ) , vec! [ ] ) ;
875
v1.workspaces.insert ( repo.workspace ( ) , wc_hash) ;
75167672 Andy McCall 1 month ago
876
let init = Format1Op {
877
parents: vec! [ ] ,
878
hlc: Hlc:: default( ) ,
879
command: " oxy init" .into ( ) ,
880
view: v1.clone ( ) ,
881
} ;
98286406 Andy McCall 1 month ago ↶
882
let init_ref = repo.store ( ) .put ( ObjectType:: Op, & init.encode ( ) ) .unwrap ( ) ;
883
v1.bookmarks.insert ( " main" .into ( ) , vec! [ h1] ) ;
75167672 Andy McCall 1 month ago
884
let commit = Format1Op {
885
parents: vec! [ init_ref] ,
886
hlc: Hlc:: default( ) ,
887
command: " oxy commit" .into ( ) ,
888
view: v1,
889
} ;
98286406 Andy McCall 1 month ago ↶
890
let commit_ref = repo.store ( ) .put ( ObjectType:: Op, & commit.encode ( ) ) .unwrap ( ) ;
891
write_atomic ( & root.join ( " .oxy/ops/head" ) , commit_ref.to_hex ( ) .as_bytes ( ) ) .unwrap ( ) ;
892
write_atomic ( & root.join ( " .oxy/format" ) , FORMAT1_LINE .as_bytes ( ) ) .unwrap ( ) ;
893
drop ( repo) ;
894
895
let repo = Repo:: open( root) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
896
assert_eq! (
897
fs:: read_to_string( root.join ( " .oxy/format" ) ) .unwrap ( ) ,
898
FORMAT_LINE
899
) ;
98286406 Andy McCall 1 month ago ↶
900
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h1] ) ;
901
let ops = repo.op_log ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
902
assert_eq! (
903
ops.iter ( )
904
.map ( | ( _, o ) | o.command.as_str ( ) )
905
.collect:: < Vec< _ > > ( ) ,
906
[ " oxy commit" , " oxy init" ]
907
) ;
98286406 Andy McCall 1 month ago ↶
908
assert_eq! ( repo.heads_of ( & ops[ 1 ] .1. view, " main" ) .unwrap ( ) , vec! [ ] ) ;
909
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
910
911
assert_eq! ( Repo:: open( root) .unwrap ( ) .op_log ( ) .unwrap ( ) .len ( ) , 2 ) ;
912
}
913
914
# [ test ]
915
fn undo_skips_snapshot_operations ( ) {
916
hermetic ( ) ;
917
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
918
let root = dir.path ( ) ;
919
let repo = Repo:: init( root) .unwrap ( ) ;
920
write ( root, " a.txt" , b " a\n " ) ;
921
let h1 = repo.commit ( " a" , & [ ] ) .unwrap ( ) ;
922
923
write ( root, " b.txt" , b " b\n " ) ;
924
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
925
assert! ( s.changed) ;
926
assert! ( repo.head_op ( ) .unwrap ( ) .1. is_snapshot ( ) ) ;
927
assert_eq! ( repo.last_command ( ) .unwrap ( ) .command, " oxy commit -m \" a\" " ) ;
928
929
repo.undo ( ) .unwrap ( ) ;
930
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ ] ) ;
931
let s = repo.snapshot ( " oxy status" ) .unwrap ( ) ;
932
let paths: Vec< _ > = s.statuses.iter ( ) .map ( | p | p.path.as_str ( ) ) .collect ( ) ;
75167672 Andy McCall 1 month ago
933
assert_eq! (
934
paths,
935
[ " a.txt" , " b.txt" ] ,
936
" both files are back in the working copy"
937
) ;
98286406 Andy McCall 1 month ago ↶
938
assert! ( repo.store ( ) .get_change ( & h1) .unwrap ( ) .deps.is_empty ( ) ) ;
939
940
assert_eq! ( repo.last_command ( ) .unwrap ( ) .command, " oxy init" ) ;
941
assert! ( repo.undo ( ) .is_err ( ) ) ;
942
}
943
944
# [ test ]
945
fn independent_changes_commute_and_make_multiple_heads ( ) {
946
hermetic ( ) ;
947
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
948
let root = dir.path ( ) ;
949
let repo = Repo:: init( root) .unwrap ( ) ;
950
write ( root, " a.txt" , b " a\n " ) ;
951
let h1 = repo.commit ( " a" , & [ ] ) .unwrap ( ) ;
952
write ( root, " b.txt" , b " b\n " ) ;
953
let h2 = repo.commit ( " b" , & [ ] ) .unwrap ( ) ;
954
let c2 = repo.store ( ) .get_change ( & h2) .unwrap ( ) ;
955
assert! ( c2.deps.is_empty ( ) , " b.txt touches nothing from h1" ) ;
956
let mut heads = repo.bookmark_heads ( ) .unwrap ( ) ;
957
heads.sort ( ) ;
958
let mut want = vec! [ h1, h2] ;
959
want.sort ( ) ;
960
assert_eq! ( heads, want) ;
961
962
write ( root, " a.txt" , b " a\n a2\n " ) ;
963
let h3 = repo.commit ( " a2" , & [ ] ) .unwrap ( ) ;
964
assert_eq! ( repo.store ( ) .get_change ( & h3) .unwrap ( ) .deps, vec! [ h1] ) ;
965
let heads: BTreeSet< _ > = repo.bookmark_heads ( ) .unwrap ( ) .into_iter ( ) .collect ( ) ;
966
assert_eq! ( heads, [ h2, h3] .into_iter ( ) .collect ( ) ) ;
967
968
write ( root, " c.txt" , b " c\n " ) ;
969
let h4 = repo.commit ( " c" , & [ h2] ) .unwrap ( ) ;
970
assert_eq! ( repo.store ( ) .get_change ( & h4) .unwrap ( ) .deps, vec! [ h2] ) ;
971
}
972
973
# [ test ]
974
fn mode_change_and_text_binary_switch ( ) {
975
hermetic ( ) ;
976
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
977
let root = dir.path ( ) ;
978
let repo = Repo:: init( root) .unwrap ( ) ;
979
write ( root, " run.sh" , b " #!/bin/sh\n " ) ;
980
repo.commit ( " add script" , & [ ] ) .unwrap ( ) ;
981
# [ cfg ( unix ) ]
982
{
983
use std:: os:: unix:: fs:: PermissionsExt;
984
fs:: set_permissions( root.join ( " run.sh" ) , fs:: Permissions:: from_mode( 0o755 ) ) .unwrap ( ) ;
985
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
986
assert_eq! (
987
statuses ( & s) ,
988
vec! [ ( " run.sh" .into ( ) , ChangeStatus:: ModeChanged) ]
989
) ;
98286406 Andy McCall 1 month ago ↶
990
let h = repo.commit ( " exec" , & [ ] ) .unwrap ( ) ;
991
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
992
assert! ( snap.files[ " run.sh" ] .mode.exec) ;
993
}
994
995
write ( root, " run.sh" , b " \0 binary" ) ;
996
let h = repo.commit ( " binary" , & [ ] ) .unwrap ( ) ;
997
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
998
assert! ( snap.files[ " run.sh" ] .mode.opaque) ;
999
write ( root, " run.sh" , b " text again\n " ) ;
1000
let h = repo.commit ( " text" , & [ ] ) .unwrap ( ) ;
1001
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
1002
assert! ( ! snap.files[ " run.sh" ] .mode.opaque) ;
75167672 Andy McCall 1 month ago
1003
assert! (
1004
matches! ( & snap.files[ " run.sh" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " text again\n " )
1005
) ;
98286406 Andy McCall 1 month ago ↶
1006
assert! ( ! snap.is_conflicted ( ) ) ;
1007
}
83e0677b Andy McCall 1 month ago ↶
1008
1009
# [ test ]
1010
fn trust_policy_governs_pushes_and_fetches ( ) {
1011
hermetic ( ) ;
1012
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1013
let root = dir.path ( ) ;
1014
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
1015
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
1016
1017
75167672 Andy McCall 1 month ago
1018
let mut a = Repo:: clone( & url, & root.join ( " a" ) )
1019
.unwrap ( )
1020
.with_home ( root.join ( " home-a" ) ) ;
1021
let ( key_a, _ ) = a
1022
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
1023
.unwrap ( ) ;
1024
let mut b = Repo:: clone( & url, & root.join ( " b" ) )
1025
.unwrap ( )
1026
.with_home ( root.join ( " home-b" ) ) ;
83e0677b Andy McCall 1 month ago ↶
1027
write ( & root.join ( " b" ) , " b.txt" , b " b\n " ) ;
1028
b.commit ( " unsigned before policy" , & [ ] ) .unwrap ( ) ;
1029
assert! ( b.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
1030
1031
1032
write ( & root.join ( " a" ) , " a.txt" , b " a\n " ) ;
1033
a.commit ( " a" , & [ ] ) .unwrap ( ) ;
1034
let sp = a.policy_init ( ) .unwrap ( ) ;
1035
assert_eq! ( sp.policy.serial, 1 ) ;
1036
a.pull ( " origin" ) .unwrap ( ) ;
1037
let results = a.push ( " origin" , false ) .unwrap ( ) ;
1038
assert! ( results.iter ( ) .all ( | r | r.ok ) , " {results:?}" ) ;
1039
assert_eq! ( origin.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial, 1 ) ;
1040
1041
1042
b.pull ( " origin" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1043
assert! (
1044
b.current_policy ( ) .unwrap ( ) .is_some ( ) ,
1045
" the policy arrived with the fetch"
1046
) ;
83e0677b Andy McCall 1 month ago ↶
1047
assert! ( b.push ( " origin" , false ) .is_err ( ) ) ;
1048
75167672 Andy McCall 1 month ago
1049
let ( key_b, id_b) = b
1050
.create_identity ( " Bob" , " [email protected] " , IdentityScope:: User)
1051
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1052
write ( & root.join ( " b" ) , " b.txt" , b " b2\n " ) ;
1053
let h_b2 = b.commit ( " b2 as Bob" , & [ ] ) .unwrap ( ) ;
1054
assert_eq! ( b.store ( ) .get_change ( & h_b2) .unwrap ( ) .author, key_b) ;
1055
let r = b.push ( " origin" , false ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1056
assert! (
1057
! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " may not update" ) ,
1058
" {:?}" ,
1059
r
1060
) ;
83e0677b Andy McCall 1 month ago ↶
1061
1062
1063
let sp = a.policy_grant ( id_b.clone ( ) , None ) .unwrap ( ) ;
1064
assert_eq! ( sp.policy.serial, 2 ) ;
1065
let r = a.push ( " origin" , false ) .unwrap ( ) ;
1066
assert! ( r.iter ( ) .any ( | x | x.name = = " policy" & & x.ok ) , " {r:?}" ) ;
1067
75167672 Andy McCall 1 month ago
1068
assert! ( b
1069
.push ( " origin" , false )
1070
.unwrap_or_else ( | e | vec! [ RefResult {
1071
name: " main" .into ( ) ,
1072
ok: false ,
1073
reason: e.to_string ( )
1074
} ] )
1075
.iter ( )
1076
.all ( | x | ! x.ok) ) ;
83e0677b Andy McCall 1 month ago ↶
1077
b.pull ( " origin" ) .unwrap ( ) ;
1078
assert_eq! ( b.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial, 2 ) ;
1079
let r = b.push ( " origin" , false ) .unwrap ( ) ;
1080
assert! ( r[ 0 ] .ok, " {:?}" , r) ;
1081
1082
let heads = origin.bookmark_heads ( ) .unwrap ( ) ;
1083
for h in & heads {
1084
let c = origin.store ( ) .get_change ( h) .unwrap ( ) ;
1085
if c.author = = key_b {
75167672 Andy McCall 1 month ago
1086
origin
1087
.change_allowed (
1088
h,
1089
& origin.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy,
1090
" main" ,
1091
)
1092
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1093
}
1094
}
1095
1096
1097
let mut forged = b.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.clone ( ) ;
1098
forged.serial = 3 ;
1099
forged.roots = vec! [ key_b] ;
1100
let fh = forged.hash ( ) ;
1101
let ( _ , sk_b) = b.secret_key ( ) .unwrap ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1102
let forged = SignedPolicy {
1103
policy: forged,
1104
sigs: vec! [ sk_b.sign ( Domain:: Policy, & key_b, & fh) ] ,
1105
} ;
1106
let r = origin
1107
.receive_push ( Bundle:: default( ) , vec! [ ] , Some ( forged) , vec! [ ] )
1108
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1109
assert! ( ! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " root" ) , " {:?}" , r) ;
1110
let stale = a.current_policy ( ) .unwrap ( ) .unwrap ( ) .1 ;
1111
let mut old = stale.clone ( ) ;
1112
old.policy.serial = 1 ;
75167672 Andy McCall 1 month ago
1113
let r = origin
1114
.receive_push ( Bundle:: default( ) , vec! [ ] , Some ( old) , vec! [ ] )
1115
.unwrap ( ) ;
83e0677b Andy McCall 1 month ago ↶
1116
assert! ( ! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " serial" ) , " {:?}" , r) ;
1117
1118
1119
a.pull ( " origin" ) .unwrap ( ) ;
1120
a.policy_revoke ( & key_b) .unwrap ( ) ;
1121
let r = a.push ( " origin" , false ) .unwrap ( ) ;
1122
assert! ( r.iter ( ) .any ( | x | x.name = = " policy" & & x.ok ) , " {r:?}" ) ;
1123
b.pull ( " origin" ) .unwrap ( ) ;
1124
write ( & root.join ( " b" ) , " b.txt" , b " b3\n " ) ;
1125
b.commit ( " b3" , & [ ] ) .unwrap ( ) ;
1126
let r = b.push ( " origin" , false ) .unwrap ( ) ;
1127
assert! ( ! r[ 0 ] .ok, " {:?}" , r) ;
1128
1129
75167672 Andy McCall 1 month ago
1130
let c = Repo:: clone( & url, & root.join ( " c" ) )
1131
.unwrap ( )
1132
.with_home ( root.join ( " home-c" ) ) ;
83e0677b Andy McCall 1 month ago ↶
1133
assert_eq! ( c.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial, 3 ) ;
75167672 Andy McCall 1 month ago
1134
assert! ( c
1135
.current_policy ( )
1136
.unwrap ( )
1137
.unwrap ( )
1138
.1
1139
.policy
1140
.roots
1141
.contains ( & key_a) ) ;
83e0677b Andy McCall 1 month ago ↶
1142
assert! ( ! c.snapshot ( " status" ) .unwrap ( ) .changed) ;
1143
}
d02e545d Andy McCall 1 month ago ↶
1144
1145
# [ test ]
1146
fn gc_packs_objects_and_history_still_works ( ) {
1147
hermetic ( ) ;
1148
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1149
let root = dir.path ( ) ;
1150
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
1151
repo.create_identity ( " Ada" , " a@b" , IdentityScope:: User)
1152
.unwrap ( ) ;
d02e545d Andy McCall 1 month ago ↶
1153
repo.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
1154
write ( root, " a.txt" , b " one\n " ) ;
1155
let h1 = repo.commit ( " one" , & [ ] ) .unwrap ( ) ;
1156
write ( root, " a.txt" , b " two\n " ) ;
1157
write ( root, " big.bin" , & vec! [ 0 u8 ; 100_000 ] ) ;
1158
let h2 = repo.commit ( " two" , & [ ] ) .unwrap ( ) ;
ba160f60 Andy McCall 1 month ago ↶
1159
1160
for i in 0 .. 5 {
1161
write ( root, " a.txt" , format! ( " two\n {i} \n " ) .as_bytes ( ) ) ;
1162
repo.snapshot ( " status" ) .unwrap ( ) ;
1163
}
1164
write ( root, " a.txt" , b " two\n " ) ;
1165
repo.snapshot ( " status" ) .unwrap ( ) ;
d02e545d Andy McCall 1 month ago ↶
1166
let before = repo.store ( ) .list ( ) .unwrap ( ) ;
1167
assert! ( repo.store ( ) .loose_count ( ) .unwrap ( ) > 0 ) ;
ba160f60 Andy McCall 1 month ago ↶
1168
1169
1170
let r = repo.gc ( true ) .unwrap ( ) ;
1171
assert_eq! ( r.packed.objects, before.len ( ) ) ;
1172
assert_eq! ( r.pruned.objects, 0 ) ;
d02e545d Andy McCall 1 month ago ↶
1173
assert_eq! ( repo.store ( ) .loose_count ( ) .unwrap ( ) , 0 ) ;
1174
assert_eq! ( repo.store ( ) .list ( ) .unwrap ( ) , before) ;
ba160f60 Andy McCall 1 month ago ↶
1175
1176
1177
1178
repo.config.set ( " core.op-retention" , " 0" ) .unwrap ( ) ;
1179
repo.save_config ( ) .unwrap ( ) ;
d02e545d Andy McCall 1 month ago ↶
1180
let repo = Repo:: open( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
ba160f60 Andy McCall 1 month ago ↶
1181
let r = repo.gc ( true ) .unwrap ( ) ;
1182
assert! ( r.pruned.objects > = 5 , " {r:?}" ) ;
1183
let after = repo.store ( ) .list ( ) .unwrap ( ) ;
1184
assert! ( after.len ( ) < before.len ( ) ) ;
1185
assert! ( after.contains ( & h1) & & after.contains ( & h2) ) ;
1186
for ( _ , op) in repo.op_log ( ) .unwrap ( ) {
1187
assert! ( ! op.command.is_empty ( ) , " operation objects are all kept" ) ;
1188
}
1189
d02e545d Andy McCall 1 month ago ↶
1190
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2] ) ;
75167672 Andy McCall 1 month ago
1191
assert! ( matches! (
1192
repo.verify_change ( & h2) .unwrap ( ) ,
1193
SignatureStatus:: Valid { .. }
1194
) ) ;
d02e545d Andy McCall 1 month ago ↶
1195
assert_eq! ( repo.log ( ) .unwrap ( ) .len ( ) , 3 ) ;
1196
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
1197
assert_eq! ( fs:: read( root.join ( " a.txt" ) ) .unwrap ( ) , b " one\n " ) ;
1198
repo.checkout ( & [ h2] , false , " checkout" ) .unwrap ( ) ;
1199
assert_eq! ( fs:: read( root.join ( " big.bin" ) ) .unwrap ( ) .len ( ) , 100_000 ) ;
1200
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
ba160f60 Andy McCall 1 month ago ↶
1201
d02e545d Andy McCall 1 month ago ↶
1202
write ( root, " a.txt" , b " three\n " ) ;
1203
repo.commit ( " three" , & [ ] ) .unwrap ( ) ;
ba160f60 Andy McCall 1 month ago ↶
1204
let r = repo.gc ( false ) .unwrap ( ) ;
1205
assert_eq! ( r.pruned.objects, 0 ) ;
1206
assert! ( r.pruned.deferred > 0 ) ;
1207
let r = repo.gc ( true ) .unwrap ( ) ;
1208
assert! ( r.pruned.objects > 0 ) ;
1209
assert_eq! ( repo.log ( ) .unwrap ( ) .len ( ) , 4 ) ;
d02e545d Andy McCall 1 month ago ↶
1210
}
614541c3 Andy McCall 1 month ago ↶
1211
1212
# [ test ]
1213
fn describe_amend_abandon_heads ( ) {
1214
hermetic ( ) ;
1215
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1216
let root = dir.path ( ) ;
1217
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
1218
repo.create_identity ( " Ada" , " a@b" , IdentityScope:: User)
1219
.unwrap ( ) ;
614541c3 Andy McCall 1 month ago ↶
1220
repo.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
1221
write ( root, " a.txt" , b " one\n " ) ;
1222
let h1 = repo.commit ( " first" , & [ ] ) .unwrap ( ) ;
1223
write ( root, " a.txt" , b " one\n two\n " ) ;
1224
let h2 = repo.commit ( " secnod" , & [ ] ) .unwrap ( ) ;
1225
let id2 = repo.store ( ) .get_change ( & h2) .unwrap ( ) .change_id;
1226
1227
1228
let h2b = repo.describe ( & h2, " second" ) .unwrap ( ) ;
1229
assert_ne! ( h2b, h2) ;
1230
let c = repo.store ( ) .get_change ( & h2b) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1231
assert_eq! (
1232
( c.change_id, c.message.as_str ( ) , c.deps.clone ( ) ) ,
1233
( id2, " second" , vec! [ h1] )
1234
) ;
614541c3 Andy McCall 1 month ago ↶
1235
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2b] ) ;
75167672 Andy McCall 1 month ago
1236
assert! ( matches! (
1237
repo.verify_change ( & h2b) .unwrap ( ) ,
1238
SignatureStatus:: Valid { .. }
1239
) ) ;
614541c3 Andy McCall 1 month ago ↶
1240
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
1241
assert! ( s.statuses.is_empty ( ) , " {:?}" , s.statuses) ;
75167672 Andy McCall 1 month ago
1242
assert_eq! (
1243
repo.resolve_change ( & id2.short ( 8 ) ) .unwrap ( ) ,
1244
h2b,
1245
" the old object is no longer reachable from the log"
1246
) ;
c07e0e7d Andy McCall 1 month ago ↶
1247
1248
let h1b = repo.describe ( & h1, " first, described" ) .unwrap ( ) ;
1249
assert_ne! ( h1b, h1) ;
1250
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
1251
assert_eq! ( heads.len ( ) , 1 ) ;
1252
let top = repo.store ( ) .get_change ( & heads[ 0 ] ) .unwrap ( ) ;
1253
assert_eq! ( ( top.change_id, top.deps.clone ( ) ) , ( id2, vec! [ h1b] ) ) ;
75167672 Andy McCall 1 month ago
1254
assert! (
1255
matches! (
1256
repo.verify_change ( & heads[ 0 ] ) .unwrap ( ) ,
1257
SignatureStatus:: Valid { .. }
1258
) ,
1259
" rewritten own change is re-signed"
1260
) ;
c07e0e7d Andy McCall 1 month ago ↶
1261
let ( _ , wc) = repo.working_copy ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1262
assert_eq! (
1263
wc.deps,
1264
vec! [ heads[ 0 ] ] ,
1265
" the working copy follows the rewrite"
1266
) ;
c07e0e7d Andy McCall 1 month ago ↶
1267
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
1268
let snap = repo.load_state ( & heads) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
1269
assert! (
1270
matches! ( & snap.files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n two\n " )
1271
) ;
614541c3 Andy McCall 1 month ago ↶
1272
1273
1274
write ( root, " a.txt" , b " one\n two\n three\n " ) ;
1275
write ( root, " b.txt" , b " b\n " ) ;
1276
let h2c = repo.amend ( None ) .unwrap ( ) ;
1277
let c = repo.store ( ) .get_change ( & h2c) .unwrap ( ) ;
1278
assert_eq! ( c.change_id, id2) ;
1279
assert_eq! ( c.message, " second" ) ;
1280
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2c] ) ;
75167672 Andy McCall 1 month ago
1281
assert_eq! (
1282
repo.log ( ) .unwrap ( ) .len ( ) ,
1283
3 ,
1284
" working copy, amended head, first"
1285
) ;
614541c3 Andy McCall 1 month ago ↶
1286
let snap = repo.load_state ( & [ h2c] ) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
1287
assert! (
1288
matches! ( & snap.files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n two\n three\n " )
1289
) ;
614541c3 Andy McCall 1 month ago ↶
1290
assert! ( snap.files.contains_key ( " b.txt" ) ) ;
1291
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
75167672 Andy McCall 1 month ago
1292
assert! (
1293
repo.amend ( None ) .is_err ( ) ,
1294
" clean working copy and no message"
1295
) ;
614541c3 Andy McCall 1 month ago ↶
1296
let h2d = repo.amend ( Some ( " second, amended" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1297
assert_eq! (
1298
repo.store ( ) .get_change ( & h2d) .unwrap ( ) .message,
1299
" second, amended"
1300
) ;
614541c3 Andy McCall 1 month ago ↶
1301
1302
1303
repo.abandon ( & h2d) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1304
assert_eq! (
1305
repo.bookmark_heads ( ) .unwrap ( ) ,
1306
vec! [ h1b] ,
1307
" the described first change is the head again"
1308
) ;
614541c3 Andy McCall 1 month ago ↶
1309
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1310
let paths: Vec< _ > = s
1311
.statuses
1312
.iter ( )
1313
.map ( | p | ( p.path.as_str ( ) , p.status) )
1314
.collect ( ) ;
1315
assert_eq! (
1316
paths,
1317
vec! [
1318
( " a.txt" , ChangeStatus:: Modified) ,
1319
( " b.txt" , ChangeStatus:: Added)
1320
]
1321
) ;
614541c3 Andy McCall 1 month ago ↶
1322
assert_eq! ( fs:: read( root.join ( " a.txt" ) ) .unwrap ( ) , b " one\n two\n three\n " ) ;
1323
let h3 = repo.commit ( " recommitted" , & [ ] ) .unwrap ( ) ;
1324
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h3] ) ;
1325
1326
repo.undo ( ) .unwrap ( ) ;
1327
repo.undo ( ) .unwrap ( ) ;
1328
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ h2d] ) ;
c07e0e7d Andy McCall 1 month ago ↶
1329
6706bdcf Andy McCall 1 month ago ↶
1330
1331
1332
c07e0e7d Andy McCall 1 month ago ↶
1333
let base = repo.bookmark_heads ( ) .unwrap ( ) [ 0 ] ;
1334
let base_change = repo.store ( ) .get_change ( & base) .unwrap ( ) ;
1335
let h1_now = base_change.deps[ 0 ] ;
6706bdcf Andy McCall 1 month ago ↶
1336
let notes = repo.abandon ( & h1_now) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1337
assert! (
1338
! notes.is_empty ( ) ,
1339
" the dependent's edit of a.txt was reified"
1340
) ;
6706bdcf Andy McCall 1 month ago ↶
1341
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
1342
let after = repo.load_state ( & heads) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
1343
assert! (
1344
! after.files.contains_key ( " a.txt" ) & & after.files.contains_key ( " b.txt" ) ,
1345
" {:?}" ,
1346
after.files.keys ( )
1347
) ;
6706bdcf Andy McCall 1 month ago ↶
1348
repo.undo ( ) .unwrap ( ) ;
1349
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ base] ) ;
c07e0e7d Andy McCall 1 month ago ↶
1350
write ( root, " c.txt" , b " c\n " ) ;
1351
let hc = repo.commit ( " c" , & [ ] ) .unwrap ( ) ;
1352
write ( root, " d.txt" , b " d\n " ) ;
1353
let hd = repo.commit ( " d" , & [ hc] ) .unwrap ( ) ;
1354
assert_eq! ( repo.store ( ) .get_change ( & hd) .unwrap ( ) .deps, vec! [ hc] ) ;
1355
repo.abandon ( & hc) .unwrap ( ) ;
1356
let heads: BTreeSet< _ > = repo.bookmark_heads ( ) .unwrap ( ) .into_iter ( ) .collect ( ) ;
1357
assert! ( ! heads.contains ( & hc) ) ;
75167672 Andy McCall 1 month ago
1358
let d_now = heads
1359
.iter ( )
1360
.find ( | h | repo.store ( ) .get_change ( h) .unwrap ( ) .message = = " d" )
1361
.copied ( )
1362
.unwrap ( ) ;
1363
assert! (
1364
repo.store ( ) .get_change ( & d_now) .unwrap ( ) .deps.is_empty ( ) ,
1365
" d inherited c's (empty) dependencies"
1366
) ;
c07e0e7d Andy McCall 1 month ago ↶
1367
let s = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1368
assert_eq! (
1369
s.statuses
1370
.iter ( )
1371
.map ( | p | p.path.as_str ( ) )
1372
.collect:: < Vec< _ > > ( ) ,
1373
vec! [ " c.txt" ] ,
1374
" c's content is now uncommitted work"
1375
) ;
614541c3 Andy McCall 1 month ago ↶
1376
}
79ab8528 Andy McCall 1 month ago ↶
1377
1378
# [ test ]
1379
fn git_export_mirrors_history ( ) {
1380
hermetic ( ) ;
75167672 Andy McCall 1 month ago
1381
if std:: process:: Command:: new( " git" )
1382
.arg ( " --version" )
1383
.output ( )
1384
.is_err ( )
1385
{
79ab8528 Andy McCall 1 month ago ↶
1386
eprintln! ( " git not installed; skipping" ) ;
1387
return ;
1388
}
1389
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1390
let root = dir.path ( ) ;
75167672 Andy McCall 1 month ago
1391
let mut repo = Repo:: init( & root.join ( " r" ) )
1392
.unwrap ( )
1393
.with_home ( root.join ( " home" ) ) ;
1394
repo.create_identity ( " Ada Lovelace" , " [email protected] " , IdentityScope:: User)
1395
.unwrap ( ) ;
79ab8528 Andy McCall 1 month ago ↶
1396
let r = root.join ( " r" ) ;
1397
write ( & r, " README.md" , b " hello\n " ) ;
1398
write ( & r, " src/main.rs" , b " fn main() {}\n " ) ;
1399
let h1 = repo.commit ( " first" , & [ ] ) .unwrap ( ) ;
1400
write ( & r, " src/main.rs" , b " fn main() { run(); }\n " ) ;
1401
write ( & r, " logo.png" , b " \x89 PNG\0 \0 " ) ;
1402
let h2 = repo.commit ( " second" , & [ ] ) .unwrap ( ) ;
1403
1404
let git_dir = root.join ( " mirror.git" ) ;
1405
let rep = repo.git_export ( & git_dir) .unwrap ( ) ;
1406
assert_eq! ( ( rep.exported, rep.skipped, rep.heads) , ( 2 , 0 , 1 ) ) ;
1407
let git = | args : & [& str ]| -> String {
75167672 Andy McCall 1 month ago
1408
let out = std:: process:: Command:: new( " git" )
1409
.arg ( " -C" )
1410
.arg ( & git_dir)
1411
.args ( args)
1412
.output ( )
1413
.unwrap ( ) ;
1414
assert! (
1415
out.status.success ( ) ,
1416
" {}" ,
1417
String :: from_utf8_lossy( & out.stderr)
1418
) ;
79ab8528 Andy McCall 1 month ago ↶
1419
String :: from_utf8_lossy( & out.stdout) .trim ( ) .to_string ( )
1420
} ;
1421
assert_eq! ( git ( & [ " log" , " --format=%s" , " main" ] ) , " second\n first" ) ;
1422
assert_eq! ( git ( & [ " show" , " main:src/main.rs" ] ) , " fn main() { run(); }" ) ;
1423
assert_eq! ( git ( & [ " show" , " main:README.md" ] ) , " hello" ) ;
1424
let body = git ( & [ " log" , " -1" , " --format=%B" , " main" ] ) ;
75167672 Andy McCall 1 month ago
1425
assert! (
1426
body.contains ( & format! ( " Oxy-Hash: {} " , h2.to_hex ( ) ) ) ,
1427
" {body}"
1428
) ;
79ab8528 Andy McCall 1 month ago ↶
1429
assert! ( body.contains ( " Change-Id: " ) , " {body}" ) ;
75167672 Andy McCall 1 month ago
1430
assert_eq! (
1431
git ( & [ " log" , " -1" , " --format=%an <%ae>" , " main" ] ) ,
1432
" Ada Lovelace <[email protected] >"
1433
) ;
1434
assert_eq! (
1435
git ( & [ " ls-tree" , " --name-only" , " main" ] ) ,
1436
" README.md\n logo.png\n src"
1437
) ;
79ab8528 Andy McCall 1 month ago ↶
1438
1439
1440
let rep = repo.git_export ( & git_dir) .unwrap ( ) ;
1441
assert_eq! ( ( rep.exported, rep.skipped) , ( 0 , 2 ) ) ;
1442
1443
1444
1445
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
1446
write ( & r, " other.txt" , b " o\n " ) ;
1447
let h3 = repo.commit ( " third" , & [ ] ) .unwrap ( ) ;
1448
let rep = repo.git_export ( & git_dir) .unwrap ( ) ;
1449
assert_eq! ( ( rep.exported, rep.heads) , ( 1 , 2 ) ) ;
1450
assert_eq! ( git ( & [ " log" , " --format=%s" , " main" ] ) , " third\n second\n first" ) ;
1451
let parents = git ( & [ " rev-list" , " --parents" , " -1" , " main" ] ) ;
1452
assert_eq! ( parents.split ( ' ' ) .count ( ) , 2 , " one parent: {parents}" ) ;
1453
assert_eq! ( git ( & [ " show" , " main:other.txt" ] ) , " o" ) ;
1454
assert_eq! ( git ( & [ " show" , " main:src/main.rs" ] ) , " fn main() { run(); }" ) ;
75167672 Andy McCall 1 month ago
1455
assert_eq! (
1456
git ( & [ " ls-tree" , " --name-only" , " main" ] ) ,
1457
" README.md\n logo.png\n other.txt\n src"
1458
) ;
79ab8528 Andy McCall 1 month ago ↶
1459
let body = git ( & [ " log" , " -1" , " --format=%B" , " main" ] ) ;
75167672 Andy McCall 1 month ago
1460
assert! (
1461
body.contains ( & format! ( " Oxy-Hash: {} " , h3.to_hex ( ) ) ) & & body.contains ( " Oxy-State: " ) ,
1462
" {body}"
1463
) ;
79ab8528 Andy McCall 1 month ago ↶
1464
1465
repo.describe ( & h3, " third, described" ) .unwrap ( ) ;
1466
let rep = repo.git_export ( & git_dir) .unwrap ( ) ;
1467
assert_eq! ( rep.exported, 1 ) ;
75167672 Andy McCall 1 month ago
1468
assert_eq! (
1469
git ( & [ " log" , " -1" , " --format=%s" , " main" ] ) ,
1470
" third, described"
1471
) ;
79ab8528 Andy McCall 1 month ago ↶
1472
assert_eq! ( git ( & [ " log" , " --format=%s" , " main" ] ) .lines ( ) .count ( ) , 4 ) ;
1473
let _ = h2;
1474
}
c3069537 Andy McCall 1 month ago ↶
1475
1476
# [ test ]
1477
fn git_import_reproduces_trees ( ) {
1478
hermetic ( ) ;
75167672 Andy McCall 1 month ago
1479
if std:: process:: Command:: new( " git" )
1480
.arg ( " --version" )
1481
.output ( )
1482
.is_err ( )
1483
{
c3069537 Andy McCall 1 month ago ↶
1484
eprintln! ( " git not installed; skipping" ) ;
1485
return ;
1486
}
1487
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1488
let root = dir.path ( ) ;
1489
let g = root.join ( " src.git" ) ;
1490
fs:: create_dir_all( & g) .unwrap ( ) ;
1491
let git = | args : & [& str ]| -> String {
1492
let out = std:: process:: Command:: new( " git" )
1493
.arg ( " -C" )
1494
.arg ( & g)
75167672 Andy McCall 1 month ago
1495
.args ( [
1496
" -c" ,
1497
" user.name=Grace" ,
1498
" -c" ,
1499
" [email protected] " ,
1500
" -c" ,
1501
" commit.gpgsign=false" ,
1502
] )
c3069537 Andy McCall 1 month ago ↶
1503
.args ( args)
1504
.output ( )
1505
.unwrap ( ) ;
75167672 Andy McCall 1 month ago
1506
assert! (
1507
out.status.success ( ) ,
1508
" git {args:?}: {}" ,
1509
String :: from_utf8_lossy( & out.stderr)
1510
) ;
c3069537 Andy McCall 1 month ago ↶
1511
String :: from_utf8_lossy( & out.stdout) .trim ( ) .to_string ( )
1512
} ;
1513
git ( & [ " init" , " -q" , " -b" , " main" ] ) ;
1514
write ( & g, " README.md" , b " # Hi\n " ) ;
1515
write ( & g, " src/lib.rs" , b " pub fn a() {}\n " ) ;
1516
git ( & [ " add" , " ." ] ) ;
1517
git ( & [ " commit" , " -q" , " -m" , " initial" ] ) ;
1518
write ( & g, " src/lib.rs" , b " pub fn a() {}\n pub fn b() {}\n " ) ;
1519
write ( & g, " run.sh" , b " #!/bin/sh\n " ) ;
1520
# [ cfg ( unix ) ]
1521
{
1522
use std:: os:: unix:: fs:: PermissionsExt;
1523
fs:: set_permissions( g.join ( " run.sh" ) , fs:: Permissions:: from_mode( 0o755 ) ) .unwrap ( ) ;
1524
}
1525
write ( & g, " logo.png" , b " \x89 PNG\0 \0 \x01 " ) ;
1526
git ( & [ " add" , " ." ] ) ;
75167672 Andy McCall 1 month ago
1527
git ( & [
1528
" commit" ,
1529
" -q" ,
1530
" -m" ,
1531
" second\n \n Change-Id: 0123456789abcdef0123456789abcdef" ,
1532
] ) ;
c3069537 Andy McCall 1 month ago ↶
1533
1534
git ( & [ " checkout" , " -q" , " -b" , " topic" , " HEAD~1" ] ) ;
1535
write ( & g, " README.md" , b " # Hi\n \n More.\n " ) ;
1536
git ( & [ " commit" , " -q" , " -am" , " readme" ] ) ;
1537
git ( & [ " checkout" , " -q" , " main" ] ) ;
1538
git ( & [ " merge" , " -q" , " --no-ff" , " -m" , " merge topic" , " topic" ] ) ;
1539
1540
1541
let initial = git ( & [ " rev-list" , " --max-parents=0" , " HEAD" ] ) ;
1542
git ( & [ " checkout" , " -q" , " -b" , " clash" , & initial] ) ;
1543
write ( & g, " src/lib.rs" , b " pub fn a() {}\n pub fn c() {}\n " ) ;
1544
git ( & [ " commit" , " -q" , " -am" , " clash" ] ) ;
1545
git ( & [ " checkout" , " -q" , " main" ] ) ;
75167672 Andy McCall 1 month ago
1546
let _ = std:: process:: Command:: new( " git" )
1547
.arg ( " -C" )
1548
.arg ( & g)
1549
.args ( [ " merge" , " -q" , " clash" ] )
1550
.output ( ) ;
1551
write (
1552
& g,
1553
" src/lib.rs" ,
1554
b " pub fn a() {}\n pub fn b() {}\n pub fn c() {}\n " ,
1555
) ;
c3069537 Andy McCall 1 month ago ↶
1556
git ( & [ " add" , " ." ] ) ;
1557
git ( & [ " commit" , " -q" , " -m" , " resolve clash" ] ) ;
1558
git ( & [ " rm" , " -q" , " logo.png" ] ) ;
1559
git ( & [ " commit" , " -q" , " -m" , " drop logo" ] ) ;
6e723b81 Andy McCall 1 month ago ↶
1560
1561
git ( & [ " mv" , " src/lib.rs" , " src/lib2.rs" ] ) ;
75167672 Andy McCall 1 month ago
1562
write (
1563
& g,
1564
" src/lib2.rs" ,
1565
b " pub fn a() {}\n pub fn b() {}\n pub fn c() {}\n pub fn d() {}\n " ,
1566
) ;
6e723b81 Andy McCall 1 month ago ↶
1567
git ( & [ " add" , " ." ] ) ;
1568
git ( & [ " commit" , " -q" , " -m" , " rename lib" ] ) ;
c3069537 Andy McCall 1 month ago ↶
1569
let expected_count: usize = git ( & [ " rev-list" , " --count" , " main" ] ) .parse ( ) .unwrap ( ) ;
1570
1571
let dest = root.join ( " oxy" ) ;
1572
let repo = Repo:: init( & dest) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
1573
let rep = repo.import_git ( & g, " main" ) .unwrap ( ) ;
1574
assert_eq! ( rep.commits, expected_count) ;
1575
assert_eq! ( rep.merges, 2 ) ;
1576
75167672 Andy McCall 1 month ago
1577
assert_eq! (
1578
fs:: read( dest.join ( " src/lib2.rs" ) ) .unwrap ( ) ,
1579
b " pub fn a() {}\n pub fn b() {}\n pub fn c() {}\n pub fn d() {}\n "
1580
) ;
6e723b81 Andy McCall 1 month ago ↶
1581
assert! ( ! dest.join ( " src/lib.rs" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
1582
assert_eq! (
1583
fs:: read( dest.join ( " README.md" ) ) .unwrap ( ) ,
1584
b " # Hi\n \n More.\n "
1585
) ;
c3069537 Andy McCall 1 month ago ↶
1586
assert! ( ! dest.join ( " logo.png" ) .exists ( ) ) ;
1587
# [ cfg ( unix ) ]
1588
{
1589
use std:: os:: unix:: fs:: PermissionsExt;
75167672 Andy McCall 1 month ago
1590
assert! (
1591
fs:: metadata( dest.join ( " run.sh" ) )
1592
.unwrap ( )
1593
.permissions ( )
1594
.mode ( )
1595
& 0o111
1596
! = 0
1597
) ;
c3069537 Andy McCall 1 month ago ↶
1598
}
75167672 Andy McCall 1 month ago
1599
assert! (
1600
! repo.snapshot ( " status" ) .unwrap ( ) .changed,
1601
" clean after import"
1602
) ;
c3069537 Andy McCall 1 month ago ↶
1603
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
1604
let st = repo.load_state ( & heads) .unwrap ( ) ;
1605
assert! ( ! st.materialize ( ) .is_conflicted ( ) ) ;
1606
assert_eq! ( st.len ( ) , expected_count) ;
75167672 Andy McCall 1 month ago
1607
let tip = repo
1608
.log ( )
1609
.unwrap ( )
1610
.into_iter ( )
1611
.find ( | e | e.change.message.starts_with ( " rename lib" ) )
1612
.unwrap ( ) ;
6e723b81 Andy McCall 1 month ago ↶
1613
let c = repo.store ( ) .get_change ( & tip.hash) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1614
assert! (
1615
c.tree_ops
1616
.iter ( )
1617
.any ( | op | matches! ( op, TreeOp:: Move { name, .. } if name = = " lib2.rs" ) ) ,
1618
" {:?}" ,
1619
c.tree_ops
1620
) ;
1621
assert! ( ! c
1622
.tree_ops
1623
.iter ( )
1624
.any ( | op | matches! ( op, TreeOp:: AddNode { .. } ) ) ) ;
c3069537 Andy McCall 1 month ago ↶
1625
1626
let log = repo.log ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1627
let second = log
1628
.iter ( )
1629
.find ( | e | e.change.message.starts_with ( " second" ) )
1630
.unwrap ( ) ;
1631
assert_eq! (
1632
second.change.change_id,
1633
ChangeId:: from_hex( " 0123456789abcdef0123456789abcdef" ) .unwrap ( )
1634
) ;
1635
assert! ( second
1636
.change
1637
.message
1638
.contains ( " Git-Author: Grace <[email protected] >" ) ) ;
1639
let resolve = log
1640
.iter ( )
1641
.find ( | e | e.change.message.starts_with ( " resolve clash" ) )
1642
.unwrap ( ) ;
1643
assert_eq! (
1644
resolve.change.kind,
1645
ChangeKind:: Resolution,
1646
" a conflicting Git merge becomes a resolution"
1647
) ;
c3069537 Andy McCall 1 month ago ↶
1648
75167672 Andy McCall 1 month ago
1649
let repo2 = Repo:: init( & root.join ( " oxy2" ) )
1650
.unwrap ( )
1651
.with_home ( root.join ( " home" ) ) ;
c3069537 Andy McCall 1 month ago ↶
1652
repo2.import_git ( & g, " main" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1653
let ids: BTreeSet< ChangeId> = repo
1654
.log ( )
1655
.unwrap ( )
1656
.iter ( )
1657
.filter ( | e | ! e.is_working_copy)
1658
.map ( | e | e.change.change_id )
1659
.collect ( ) ;
1660
let ids2: BTreeSet< ChangeId> = repo2
1661
.log ( )
1662
.unwrap ( )
1663
.iter ( )
1664
.filter ( | e | ! e.is_working_copy)
1665
.map ( | e | e.change.change_id )
1666
.collect ( ) ;
c3069537 Andy McCall 1 month ago ↶
1667
assert_eq! ( ids, ids2) ;
1668
1669
let mirror = root.join ( " mirror.git" ) ;
1670
repo.git_export ( & mirror) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1671
let out = std:: process:: Command:: new( " git" )
1672
.arg ( " -C" )
1673
.arg ( & mirror)
1674
.args ( [ " show" , " main:src/lib2.rs" ] )
1675
.output ( )
1676
.unwrap ( ) ;
1677
assert_eq! (
1678
String :: from_utf8_lossy( & out.stdout) ,
1679
" pub fn a() {}\n pub fn b() {}\n pub fn c() {}\n pub fn d() {}\n "
1680
) ;
1681
let out = std:: process:: Command:: new( " git" )
1682
.arg ( " -C" )
1683
.arg ( & mirror)
1684
.args ( [ " show" , " main:src/lib.rs" ] )
1685
.output ( )
1686
.unwrap ( ) ;
1687
assert! (
1688
! out.status.success ( ) ,
1689
" the old path is gone from the export"
1690
) ;
c3069537 Andy McCall 1 month ago ↶
1691
}
252e8cb2 Andy McCall 1 month ago ↶
1692
1693
# [ test ]
1694
fn checkpoints_accelerate_loading_and_stay_correct ( ) {
1695
hermetic ( ) ;
1696
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1697
let root = dir.path ( ) ;
1698
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
1699
repo.config.set ( " core.checkpoint-interval" , " 5" ) .unwrap ( ) ;
1700
repo.save_config ( ) .unwrap ( ) ;
1701
let repo = Repo:: open( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
1702
let mut hashes = Vec :: new( ) ;
1703
for i in 0 .. 12 {
75167672 Andy McCall 1 month ago
1704
write (
1705
root,
1706
" a.txt" ,
1707
format! (
1708
" {} \n " ,
1709
( 0 .. = i)
1710
.map ( | k | k.to_string ( ) )
1711
.collect:: < Vec< _ > > ( )
1712
.join ( " \n " )
1713
)
1714
.as_bytes ( ) ,
1715
) ;
252e8cb2 Andy McCall 1 month ago ↶
1716
write ( root, & format! ( " f{i} .txt" ) , b " x\n " ) ;
1717
hashes.push ( repo.commit ( & format! ( " c{i} " ) , & [ ] ) .unwrap ( ) ) ;
1718
}
75167672 Andy McCall 1 month ago
1719
let ( _ , cp) = repo
1720
.latest_checkpoint ( " main" )
1721
.unwrap ( )
1722
.expect ( " automatic checkpoint" ) ;
252e8cb2 Andy McCall 1 month ago ↶
1723
assert! ( cp.count > = 5 & & cp.count < = 12 , " {}" , cp.count) ;
1724
1725
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
1726
let fast = repo.load_state ( & heads) .unwrap ( ) ;
1727
let full = repo.load_state_full ( & heads) .unwrap ( ) ;
1728
assert_eq! ( fast.len ( ) , full.len ( ) ) ;
1729
assert_eq! ( fast.heads ( ) , full.heads ( ) ) ;
1730
let ( a, b) = ( fast.materialize ( ) , full.materialize ( ) ) ;
75167672 Andy McCall 1 month ago
1731
assert_eq! (
1732
a.files.keys ( ) .collect:: < Vec< _ > > ( ) ,
1733
b.files.keys ( ) .collect:: < Vec< _ > > ( )
1734
) ;
252e8cb2 Andy McCall 1 month ago ↶
1735
for ( p, f) in & a.files {
1736
assert_eq! ( f.content, b.files[ p] .content, " {p}" ) ;
1737
}
1738
assert! ( repo.load_state_checkpointed ( & heads) .unwrap ( ) .is_some ( ) ) ;
1739
75167672 Andy McCall 1 month ago
1740
assert! ( repo
1741
.load_state_checkpointed ( & [ hashes[ 0 ] ] )
1742
.unwrap ( )
1743
.is_none ( ) ) ;
252e8cb2 Andy McCall 1 month ago ↶
1744
assert_eq! ( repo.load_state ( & [ hashes[ 0 ] ] ) .unwrap ( ) .len ( ) , 1 ) ;
1745
1746
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
1747
let h = repo.describe ( & hashes[ 3 ] , " c3 described" ) .unwrap ( ) ;
1748
assert_eq! ( repo.store ( ) .get_change ( & h) .unwrap ( ) .message, " c3 described" ) ;
1749
let ( r, n, bytes) = repo.checkpoint ( ) .unwrap ( ) ;
1750
assert! ( n > = 12 & & bytes > 0 ) ;
1751
assert_eq! ( repo.latest_checkpoint ( " main" ) .unwrap ( ) .unwrap ( ) .0 , r) ;
1752
write ( root, " a.txt" , b " after\n " ) ;
1753
let h_after = repo.commit ( " after" , & [ ] ) .unwrap ( ) ;
1754
let st = repo.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) ) .unwrap ( ) ;
1755
assert! ( st.contains ( & h_after) ) ;
75167672 Andy McCall 1 month ago
1756
assert! (
1757
matches! ( & st.materialize ( ) .files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " after\n " )
1758
) ;
252e8cb2 Andy McCall 1 month ago ↶
1759
assert_eq! ( repo.log ( ) .unwrap ( ) .len ( ) , 14 ) ;
1760
1761
repo.gc ( true ) .unwrap ( ) ;
1762
assert! ( repo.latest_checkpoint ( " main" ) .unwrap ( ) .is_some ( ) ) ;
75167672 Andy McCall 1 month ago
1763
assert! ( repo
1764
.load_state_checkpointed ( & repo.bookmark_heads ( ) .unwrap ( ) )
1765
.unwrap ( )
1766
.is_some ( ) ) ;
252e8cb2 Andy McCall 1 month ago ↶
1767
}
135212de Andy McCall 1 month ago ↶
1768
1769
# [ test ]
1770
fn quick_status_for_prompts ( ) {
1771
hermetic ( ) ;
1772
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1773
let root = dir.path ( ) ;
1774
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
1775
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
1776
let a = Repo:: clone( & url, & root.join ( " a" ) )
1777
.unwrap ( )
1778
.with_home ( root.join ( " home" ) ) ;
135212de Andy McCall 1 month ago ↶
1779
let ops_before = a.op_log ( ) .unwrap ( ) .len ( ) ;
1780
let info = a.quick_status ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1781
assert_eq! (
1782
(
1783
info.bookmark.as_str ( ) ,
1784
info.heads,
1785
info.dirty,
1786
info.conflicted,
1787
info.ahead,
1788
info.behind
1789
) ,
1790
( " main" , 0 , false , false , Some ( 0 ) , Some ( 0 ) )
1791
) ;
135212de Andy McCall 1 month ago ↶
1792
write ( & root.join ( " a" ) , " f.txt" , b " f\n " ) ;
1793
assert! ( a.quick_status ( ) .unwrap ( ) .dirty, " a new file is dirty" ) ;
75167672 Andy McCall 1 month ago
1794
assert_eq! (
1795
a.op_log ( ) .unwrap ( ) .len ( ) ,
1796
ops_before,
1797
" quick_status records nothing"
1798
) ;
135212de Andy McCall 1 month ago ↶
1799
1800
a.snapshot ( " status" ) .unwrap ( ) ;
1801
assert! ( a.quick_status ( ) .unwrap ( ) .dirty) ;
1802
a.commit ( " f" , & [ ] ) .unwrap ( ) ;
1803
let info = a.quick_status ( ) .unwrap ( ) ;
1804
assert! ( ! info.dirty, " clean right after a commit" ) ;
1805
assert_eq! ( ( info.ahead, info.behind) , ( Some ( 1 ) , Some ( 0 ) ) ) ;
75167672 Andy McCall 1 month ago
1806
assert_eq! (
1807
info.format ( " %b %c%d%x%A%D" ) ,
1808
format! ( " main {} +1" , info.change_id)
1809
) ;
135212de Andy McCall 1 month ago ↶
1810
assert_eq! ( info.format ( " [%h heads] %a/%B %%" ) , " [1 heads] 1/0 %" ) ;
1811
1812
1813
let p = root.join ( " a/f.txt" ) ;
1814
let content = fs:: read( & p) .unwrap ( ) ;
1815
fs:: write( & p, & content) .unwrap ( ) ;
1816
assert! ( ! a.quick_status ( ) .unwrap ( ) .dirty) ;
1817
fs:: write( & p, b " g\n " ) .unwrap ( ) ;
1818
assert! ( a.quick_status ( ) .unwrap ( ) .dirty) ;
1819
fs:: write( & p, b " f\n " ) .unwrap ( ) ;
1820
1821
assert! ( a.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
1822
assert_eq! ( a.quick_status ( ) .unwrap ( ) .ahead, Some ( 0 ) ) ;
75167672 Andy McCall 1 month ago
1823
let b = Repo:: clone( & url, & root.join ( " b" ) )
1824
.unwrap ( )
1825
.with_home ( root.join ( " home-b" ) ) ;
135212de Andy McCall 1 month ago ↶
1826
write ( & root.join ( " b" ) , " g.txt" , b " g\n " ) ;
1827
b.commit ( " g" , & [ ] ) .unwrap ( ) ;
1828
b.push ( " origin" , false ) .unwrap ( ) ;
1829
a.fetch ( " origin" ) .unwrap ( ) ;
1830
let info = a.quick_status ( ) .unwrap ( ) ;
1831
assert_eq! ( ( info.ahead, info.behind) , ( Some ( 0 ) , Some ( 1 ) ) ) ;
1832
assert_eq! ( info.format ( " %A%D" ) , " -1" ) ;
1833
let _ = origin;
1834
}
b3c5a59a Andy McCall 1 month ago ↶
1835
1836
# [ test ]
1837
fn lazy_blobs_are_fetched_on_first_read ( ) {
1838
hermetic ( ) ;
1839
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1840
let root = dir.path ( ) ;
1841
let origin_dir = root.join ( " origin" ) ;
75167672 Andy McCall 1 month ago
1842
let origin = Repo:: init( & origin_dir)
1843
.unwrap ( )
1844
.with_home ( root.join ( " home" ) ) ;
b3c5a59a Andy McCall 1 month ago ↶
1845
let url = origin_dir.to_string_lossy ( ) .to_string ( ) ;
1846
let big: Vec< u8 > = ( 0 .. 200_000 u32 ) .map ( | i | ( i % 251 ) as u8 ) .collect ( ) ;
1847
write ( & origin_dir, " big.bin" , & big) ;
1848
write ( & origin_dir, " small.bin" , & [ 0 u8 , 1 , 2 ] ) ;
1849
write ( & origin_dir, " t.txt" , b " t\n " ) ;
1850
let h = origin.commit ( " blobs" , & [ ] ) .unwrap ( ) ;
1851
let big_ref = Repo:: blob_ref( & big) ;
1852
1853
75167672 Andy McCall 1 month ago
1854
let a = Repo:: init_with_id( & root.join ( " a" ) , origin.id ( ) .unwrap ( ) )
1855
.unwrap ( )
1856
.with_home ( root.join ( " home" ) ) ;
b3c5a59a Andy McCall 1 month ago ↶
1857
let a = {
1858
let mut a = a;
1859
a.config.set ( " remote.origin.url" , & url) .unwrap ( ) ;
1860
a.config.set ( " filter.max-blob" , " 100K" ) .unwrap ( ) ;
1861
a.save_config ( ) .unwrap ( ) ;
1862
a
1863
} ;
1864
assert_eq! ( a.config.max_blob, Some ( 100 * 1024 ) ) ;
1865
a.fetch ( " origin" ) .unwrap ( ) ;
1866
assert! ( a.store ( ) .has ( & h) ) ;
1867
assert! ( ! a.store ( ) .has ( & big_ref) , " withheld" ) ;
75167672 Andy McCall 1 month ago
1868
assert! (
1869
a.store ( ) .has ( & Repo:: blob_ref( & [ 0 u8 , 1 , 2 ] ) ) ,
1870
" small blob came along"
1871
) ;
b3c5a59a Andy McCall 1 month ago ↶
1872
assert_eq! ( a.lazy_remote ( & big_ref) .as_deref ( ) , Some ( " origin" ) ) ;
1873
1874
1875
let ( op_ref, op) = a.head_op ( ) .unwrap ( ) ;
1876
let mut view = op.view.clone ( ) ;
75167672 Andy McCall 1 month ago
1877
view.bookmarks.insert (
1878
" main" .into ( ) ,
1879
a.store ( ) .put_state ( & StateObject:: new( vec! [ h] ) ) .unwrap ( ) ,
1880
) ;
b3c5a59a Andy McCall 1 month ago ↶
1881
a.record_op ( & [ op_ref] , " adopt" , view) .unwrap ( ) ;
1882
1883
a.checkout ( & [ h] , true , " checkout" ) .unwrap ( ) ;
1884
assert! ( a.store ( ) .has ( & big_ref) , " fetched on first read" ) ;
1885
assert_eq! ( fs:: read( root.join ( " a/big.bin" ) ) .unwrap ( ) , big) ;
1886
assert! ( a.lazy_remote ( & big_ref) .is_none ( ) , " no longer registered" ) ;
1887
write ( & root.join ( " a" ) , " t.txt" , b " t2\n " ) ;
1888
a.commit ( " text edit" , & [ ] ) .unwrap ( ) ;
1889
1890
7421309f Andy McCall 1 month ago ↶
1891
let b = Repo:: clone_with( & url, & root.join ( " b" ) , Some ( 1024 ) , & [ ] ) .unwrap ( ) ;
b3c5a59a Andy McCall 1 month ago ↶
1892
assert_eq! ( b.config.max_blob, Some ( 1024 ) ) ;
1893
assert_eq! ( fs:: read( root.join ( " b/big.bin" ) ) .unwrap ( ) , big) ;
1894
assert_eq! ( parse_size ( " 2M" ) .unwrap ( ) , 2 < < 20 ) ;
1895
assert! ( parse_size ( " lots" ) .is_err ( ) ) ;
1896
}
7421309f Andy McCall 1 month ago ↶
1897
1898
# [ test ]
1899
fn sparse_checkout_materializes_only_chosen_paths ( ) {
1900
hermetic ( ) ;
1901
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1902
let root = dir.path ( ) ;
1903
let origin_dir = root.join ( " origin" ) ;
75167672 Andy McCall 1 month ago
1904
let origin = Repo:: init( & origin_dir)
1905
.unwrap ( )
1906
.with_home ( root.join ( " home" ) ) ;
7421309f Andy McCall 1 month ago ↶
1907
write ( & origin_dir, " src/main.rs" , b " fn main() {}\n " ) ;
1908
write ( & origin_dir, " src/lib.rs" , b " pub fn a() {}\n " ) ;
1909
write ( & origin_dir, " docs/guide.md" , b " # guide\n " ) ;
1910
let logo: Vec< u8 > = ( 0 .. 50_000 u32 ) .map ( | i | ( i % 7 ) as u8 ) .collect ( ) ;
1911
write ( & origin_dir, " docs/logo.png" , & logo) ;
1912
write ( & origin_dir, " README.md" , b " # top\n " ) ;
1913
let h1 = origin.commit ( " tree" , & [ ] ) .unwrap ( ) ;
1914
let url = origin_dir.to_string_lossy ( ) .to_string ( ) ;
1915
75167672 Andy McCall 1 month ago
1916
let a = Repo:: clone_with( & url, & root.join ( " a" ) , None , & [ " src" .into ( ) ] )
1917
.unwrap ( )
1918
.with_home ( root.join ( " home" ) ) ;
7421309f Andy McCall 1 month ago ↶
1919
assert_eq! ( a.config.sparse_paths, vec! [ " src" ] ) ;
1920
assert_eq! ( a.config.max_blob, Some ( 0 ) , " sparse implies lazy blobs" ) ;
1921
assert! ( root.join ( " a/src/main.rs" ) .exists ( ) ) ;
1922
assert! ( ! root.join ( " a/docs" ) .exists ( ) & & ! root.join ( " a/README.md" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
1923
assert! (
1924
! a.store ( ) .has ( & Repo:: blob_ref( & logo) ) ,
1925
" out-of-scope blob never fetched"
1926
) ;
1927
assert! (
1928
! a.snapshot ( " status" ) .unwrap ( ) .changed,
1929
" out-of-scope files are not removals"
1930
) ;
7421309f Andy McCall 1 month ago ↶
1931
assert_eq! ( a.quick_status ( ) .unwrap ( ) .dirty, false ) ;
1932
1933
75167672 Andy McCall 1 month ago
1934
write (
1935
& root.join ( " a" ) ,
1936
" src/lib.rs" ,
1937
b " pub fn a() {}\n pub fn b() {}\n " ,
1938
) ;
7421309f Andy McCall 1 month ago ↶
1939
let h2 = a.commit ( " edit lib" , & [ ] ) .unwrap ( ) ;
1940
let st = a.load_state ( & [ h2] ) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
1941
assert! (
1942
st.files.contains_key ( " docs/guide.md" ) ,
1943
" the state still has everything"
1944
) ;
7421309f Andy McCall 1 month ago ↶
1945
assert! ( a.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
1946
1947
write ( & root.join ( " a" ) , " docs/new.md" , b " ignored\n " ) ;
1948
assert! ( a.snapshot ( " status" ) .unwrap ( ) .statuses.is_empty ( ) ) ;
1949
1950
1951
let mut a = a;
1952
a.config.set ( " filter.paths" , " src,docs" ) .unwrap ( ) ;
1953
a.save_config ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1954
let r = a
1955
.checkout ( & a.bookmark_heads ( ) .unwrap ( ) , true , " widen" )
1956
.unwrap ( ) ;
7421309f Andy McCall 1 month ago ↶
1957
assert! ( r.written > = 2 ) ;
75167672 Andy McCall 1 month ago
1958
assert_eq! (
1959
fs:: read( root.join ( " a/docs/logo.png" ) ) .unwrap ( ) ,
1960
logo,
1961
" now fetched lazily"
1962
) ;
7421309f Andy McCall 1 month ago ↶
1963
assert! ( ! root.join ( " a/README.md" ) .exists ( ) ) ;
1964
1965
origin.pull ( " origin" ) .err ( ) ;
75167672 Andy McCall 1 month ago
1966
let full = origin
1967
.load_state ( & origin.bookmark_heads ( ) .unwrap ( ) )
1968
.unwrap ( )
1969
.materialize ( ) ;
1970
assert! (
1971
matches! ( & full.files[ " src/lib.rs" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " pub fn a() {}\n pub fn b() {}\n " )
1972
) ;
7421309f Andy McCall 1 month ago ↶
1973
assert! ( full.files.contains_key ( " README.md" ) ) ;
1974
let _ = h1;
1975
}
2863456a Andy McCall 1 month ago ↶
1976
1977
# [ test ]
1978
fn shallow_clone_and_deepen ( ) {
1979
hermetic ( ) ;
1980
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1981
let root = dir.path ( ) ;
1982
let origin_dir = root.join ( " origin" ) ;
75167672 Andy McCall 1 month ago
1983
let origin = Repo:: init( & origin_dir)
1984
.unwrap ( )
1985
.with_home ( root.join ( " home" ) ) ;
2863456a Andy McCall 1 month ago ↶
1986
let mut hashes = Vec :: new( ) ;
1987
for i in 0 .. 8 {
75167672 Andy McCall 1 month ago
1988
write (
1989
& origin_dir,
1990
" a.txt" ,
1991
format! (
1992
" {} \n " ,
1993
( 0 .. = i)
1994
.map ( | k | k.to_string ( ) )
1995
.collect:: < Vec< _ > > ( )
1996
.join ( " \n " )
1997
)
1998
.as_bytes ( ) ,
1999
) ;
2863456a Andy McCall 1 month ago ↶
2000
hashes.push ( origin.commit ( & format! ( " c{i} " ) , & [ ] ) .unwrap ( ) ) ;
2001
}
2002
let url = origin_dir.to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
2003
let a = Repo:: clone_shallow( & url, & root.join ( " a" ) , 3 , None , & [ ] )
2004
.unwrap ( )
2005
.with_home ( root.join ( " home" ) ) ;
2863456a Andy McCall 1 month ago ↶
2006
assert! ( a.is_shallow ( ) ) ;
75167672 Andy McCall 1 month ago
2007
assert_eq! (
2008
fs:: read_to_string( root.join ( " a/a.txt" ) ) .unwrap ( ) ,
2009
" 0\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n "
2010
) ;
2011
assert! (
2012
a.store ( ) .has ( & hashes[ 7 ] ) & & a.store ( ) .has ( & hashes[ 5 ] ) ,
2013
" three levels present"
2014
) ;
2863456a Andy McCall 1 month ago ↶
2015
assert! ( ! a.store ( ) .has ( & hashes[ 4 ] ) , " beyond the depth is absent" ) ;
2016
assert_eq! ( a.boundary ( ) , [ hashes[ 4 ] ] .into_iter ( ) .collect ( ) ) ;
75167672 Andy McCall 1 month ago
2017
assert_eq! (
2018
a.log ( ) .unwrap ( ) .len ( ) ,
2019
9 ,
2020
" the log shows every change from the checkpoint's metadata"
2021
) ;
2863456a Andy McCall 1 month ago ↶
2022
assert! ( ! a.snapshot ( " status" ) .unwrap ( ) .changed) ;
2023
assert_eq! ( a.quick_status ( ) .unwrap ( ) .heads, 1 ) ;
2024
2025
2026
write ( & root.join ( " a" ) , " a.txt" , b " 0\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n " ) ;
2027
let h_new = a.commit ( " c8" , & [ ] ) .unwrap ( ) ;
2028
assert! ( a.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
2029
assert_eq! ( origin.bookmark_heads ( ) .unwrap ( ) , vec! [ h_new] ) ;
2030
2031
write ( & origin_dir, " b.txt" , b " b\n " ) ;
2032
origin.pull ( " origin" ) .err ( ) ;
2033
let ( _ , wc) = origin.working_copy ( ) .unwrap ( ) ;
2034
let _ = wc;
75167672 Andy McCall 1 month ago
2035
origin
2036
.checkout ( & origin.bookmark_heads ( ) .unwrap ( ) , true , " co" )
2037
.unwrap ( ) ;
2863456a Andy McCall 1 month ago ↶
2038
write ( & origin_dir, " b.txt" , b " b\n " ) ;
2039
let h_b = origin.commit ( " b" , & [ ] ) .unwrap ( ) ;
2040
let pr = a.pull ( " origin" ) .unwrap ( ) ;
2041
assert! ( pr.changed) ;
2042
assert! ( a.store ( ) .has ( & h_b) ) ;
2043
assert! ( root.join ( " a/b.txt" ) .exists ( ) ) ;
2044
2045
2046
2047
2048
assert! ( a.deepen ( " origin" , 2 ) .unwrap ( ) > = 2 ) ;
2049
assert! ( a.store ( ) .has ( & hashes[ 3 ] ) ) ;
2050
assert! ( a.is_shallow ( ) ) ;
2051
a.deepen ( " origin" , 100 ) .unwrap ( ) ;
2052
assert! ( ! a.is_shallow ( ) , " boundary reached the root" ) ;
2053
assert! ( a.store ( ) .has ( & hashes[ 0 ] ) ) ;
75167672 Andy McCall 1 month ago
2054
assert_eq! (
2055
a.load_state_full ( & a.bookmark_heads ( ) .unwrap ( ) )
2056
.unwrap ( )
2057
.len ( ) ,
2058
10
2059
) ;
2863456a Andy McCall 1 month ago ↶
2060
}
132b884b Andy McCall 1 month ago ↶
2061
2062
# [ test ]
2063
fn tags_are_immutable_named_states_and_travel ( ) {
2064
hermetic ( ) ;
2065
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2066
let root = dir.path ( ) ;
2067
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
2068
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
2069
let mut a = Repo:: clone( & url, & root.join ( " a" ) )
2070
.unwrap ( )
2071
.with_home ( root.join ( " home-a" ) ) ;
2072
let ( key_a, _ ) = a
2073
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
2074
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2075
write ( & root.join ( " a" ) , " f.txt" , b " 1\n " ) ;
2076
let h1 = a.commit ( " one" , & [ ] ) .unwrap ( ) ;
2077
write ( & root.join ( " a" ) , " f.txt" , b " 1\n 2\n " ) ;
2078
let h2 = a.commit ( " two" , & [ ] ) .unwrap ( ) ;
2079
2080
let t = a.tag_create ( " v1" , & [ h1] , " first release" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2081
assert_eq! (
2082
( t.heads.clone ( ) , t.signer.as_deref ( ) ) ,
2083
( vec! [ h1] , Some ( " Ada" ) )
2084
) ;
132b884b Andy McCall 1 month ago ↶
2085
assert! ( a.tag_create ( " v1" , & [ h2] , " " ) .is_err ( ) , " immutable" ) ;
2086
assert! ( a.tag_create ( " bad/name" , & [ h2] , " " ) .is_err ( ) ) ;
2087
assert_eq! ( a.tag_heads ( " v1" ) .unwrap ( ) , Some ( vec! [ h1] ) ) ;
2088
assert_eq! ( a.resolve_heads ( " v1" ) .unwrap ( ) , vec! [ h1] ) ;
2089
assert_eq! ( a.resolve_heads ( " main" ) .unwrap ( ) , vec! [ h2] ) ;
2090
let ( _ , sa) = a.tag_attest ( " v1" ) .unwrap ( ) .unwrap ( ) ;
2091
assert_eq! ( sa.sigs[ 0 ] .key, key_a) ;
2092
75167672 Andy McCall 1 month ago
2093
a.checkout ( & a.resolve_heads ( " v1" ) .unwrap ( ) , false , " co" )
2094
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2095
assert_eq! ( fs:: read( root.join ( " a/f.txt" ) ) .unwrap ( ) , b " 1\n " ) ;
75167672 Andy McCall 1 month ago
2096
a.checkout ( & a.bookmark_heads ( ) .unwrap ( ) , false , " co" )
2097
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2098
2099
2100
let r = a.push ( " origin" , false ) .unwrap ( ) ;
2101
assert! ( r.iter ( ) .any ( | x | x.name = = " tags/v1" & & x.ok ) , " {r:?}" ) ;
2102
assert_eq! ( origin.tag_heads ( " v1" ) .unwrap ( ) , Some ( vec! [ h1] ) ) ;
75167672 Andy McCall 1 month ago
2103
let mut forged = a
2104
.tag_refs ( )
2105
.unwrap ( )
2106
.into_iter ( )
2107
.find ( | t | t.name = = " v1" )
2108
.unwrap ( ) ;
2109
forged.state = origin
2110
.store ( )
2111
.put_state ( & StateObject:: new( vec! [ h2] ) )
2112
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2113
forged.heads = vec! [ h2] ;
2114
forged.attestation = None ;
75167672 Andy McCall 1 month ago
2115
let r = origin
2116
.receive_push ( Bundle:: default( ) , vec! [ ] , None , vec! [ forged] )
2117
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2118
assert! ( ! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " immutable" ) , " {r:?}" ) ;
2119
2120
75167672 Andy McCall 1 month ago
2121
let b = Repo:: clone( & url, & root.join ( " b" ) )
2122
.unwrap ( )
2123
.with_home ( root.join ( " home-b" ) ) ;
132b884b Andy McCall 1 month ago ↶
2124
assert_eq! ( b.tag_heads ( " v1" ) .unwrap ( ) , Some ( vec! [ h1] ) ) ;
2125
assert! ( b.tags ( ) .unwrap ( ) [ 0 ] .signer.as_deref ( ) = = Some ( " Ada" ) ) ;
2126
a.tag_create ( " v2" , & [ h2] , " second" ) .unwrap ( ) ;
2127
a.push ( " origin" , false ) .unwrap ( ) ;
2128
let rep = b.fetch ( " origin" ) .unwrap ( ) ;
2129
assert_eq! ( rep.new_tags, 1 ) ;
2130
assert_eq! ( b.tag_heads ( " v2" ) .unwrap ( ) , Some ( vec! [ h2] ) ) ;
2131
2132
2133
a.policy_init ( ) .unwrap ( ) ;
2134
a.push ( " origin" , false ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2135
let unattested = crate :: protocol:: TagRef {
2136
name: " v3" .into ( ) ,
2137
state: origin
2138
.store ( )
2139
.put_state ( & StateObject:: new( vec! [ h2] ) )
2140
.unwrap ( ) ,
2141
heads: vec! [ h2] ,
2142
attestation: None ,
2143
} ;
2144
let r = origin
2145
.receive_push ( Bundle:: default( ) , vec! [ ] , None , vec! [ unattested] )
2146
.unwrap ( ) ;
132b884b Andy McCall 1 month ago ↶
2147
assert! ( ! r[ 0 ] .ok & & r[ 0 ] .reason.contains ( " attestation" ) , " {r:?}" ) ;
2148
a.tag_create ( " v3" , & [ h2] , " " ) .unwrap ( ) ;
2149
let r = a.push ( " origin" , false ) .unwrap ( ) ;
2150
assert! ( r.iter ( ) .any ( | x | x.name = = " tags/v3" & & x.ok ) , " {r:?}" ) ;
2151
assert! ( a.gc ( true ) .is_ok ( ) ) ;
2152
assert_eq! ( a.tags ( ) .unwrap ( ) .len ( ) , 3 , " tags survive gc" ) ;
2153
}
4e88e774 Andy McCall 1 month ago ↶
2154
2155
# [ test ]
2156
fn multiple_workspaces_share_a_repository ( ) {
2157
hermetic ( ) ;
2158
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2159
let root = dir.path ( ) ;
2160
let main_dir = root.join ( " main" ) ;
2161
let repo = Repo:: init( & main_dir) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
2162
write ( & main_dir, " a.txt" , b " a\n " ) ;
2163
let h1 = repo.commit ( " a" , & [ ] ) .unwrap ( ) ;
2164
assert_eq! ( repo.workspaces ( ) .unwrap ( ) .len ( ) , 1 ) ;
2165
2166
2167
let ws_dir = root.join ( " ws2" ) ;
2168
let ws = repo.workspace_add ( & ws_dir, None ) .unwrap ( ) ;
2169
assert! ( ws_dir.join ( " .oxy" ) .is_file ( ) ) ;
2170
assert_eq! ( fs:: read( ws_dir.join ( " a.txt" ) ) .unwrap ( ) , b " a\n " ) ;
2171
assert_ne! ( ws.workspace ( ) , repo.workspace ( ) ) ;
2172
let list = repo.workspaces ( ) .unwrap ( ) ;
2173
assert_eq! ( list.len ( ) , 2 ) ;
75167672 Andy McCall 1 month ago
2174
assert! ( list
2175
.iter ( )
2176
.any ( | w | w.id = = ws.workspace ( ) & & w.path = = ws_dir.canonicalize ( ) .unwrap ( ) & & ! w.current ) ) ;
4e88e774 Andy McCall 1 month ago ↶
2177
assert! ( ! ws.snapshot ( " status" ) .unwrap ( ) .changed) ;
2178
2179
2180
write ( & ws_dir, " b.txt" , b " b\n " ) ;
2181
let s = ws.snapshot ( " status" ) .unwrap ( ) ;
2182
assert_eq! ( s.statuses.len ( ) , 1 ) ;
75167672 Andy McCall 1 month ago
2183
assert! (
2184
! repo.snapshot ( " status" ) .unwrap ( ) .changed,
2185
" the first workspace is untouched"
2186
) ;
4e88e774 Andy McCall 1 month ago ↶
2187
let h2 = ws.commit ( " b from ws2" , & [ ] ) .unwrap ( ) ;
2188
let mut heads = repo.bookmark_heads ( ) .unwrap ( ) ;
2189
heads.sort ( ) ;
2190
let mut want = vec! [ h1, h2] ;
2191
want.sort ( ) ;
2192
assert_eq! ( heads, want, " both workspaces see the bookmark move" ) ;
2193
assert! ( ! main_dir.join ( " b.txt" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
2194
repo.checkout ( & repo.bookmark_heads ( ) .unwrap ( ) , false , " co" )
2195
.unwrap ( ) ;
4e88e774 Andy McCall 1 month ago ↶
2196
assert! ( main_dir.join ( " b.txt" ) .exists ( ) ) ;
2197
2198
2199
let reopened = Repo:: open( & ws_dir) .unwrap ( ) ;
2200
assert_eq! ( reopened.workspace ( ) , ws.workspace ( ) ) ;
2201
assert_eq! ( reopened.id ( ) .unwrap ( ) , repo.id ( ) .unwrap ( ) ) ;
2202
assert_eq! ( reopened.quick_status ( ) .unwrap ( ) .heads, 2 ) ;
2203
2204
2205
assert! ( repo.workspace_add ( & main_dir.join ( " inner" ) , None ) .is_err ( ) ) ;
2206
assert! ( repo.workspace_forget ( & repo.workspace ( ) ) .is_err ( ) ) ;
2207
repo.workspace_forget ( & ws.workspace ( ) ) .unwrap ( ) ;
2208
assert_eq! ( repo.workspaces ( ) .unwrap ( ) .len ( ) , 1 ) ;
2209
assert! ( ws_dir.join ( " b.txt" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
2210
assert! (
2211
ws.snapshot ( " status" ) .is_err ( ) ,
2212
" a forgotten workspace is no longer in the view"
2213
) ;
4e88e774 Andy McCall 1 month ago ↶
2214
2215
2216
let lock = repo.lock_snapshot ( ) .unwrap ( ) ;
2217
drop ( lock) ;
75167672 Andy McCall 1 month ago
2218
let lock_path = main_dir
2219
.join ( " .oxy/workspaces" )
2220
.join ( repo.workspace ( ) .to_hex ( ) )
2221
.join ( " lock" ) ;
4e88e774 Andy McCall 1 month ago ↶
2222
fs:: write( & lock_path, " 1" ) .unwrap ( ) ;
2223
let old = std:: time:: SystemTime:: now( ) - std:: time:: Duration:: from_secs( 120 ) ;
75167672 Andy McCall 1 month ago
2224
fs:: File:: open( & lock_path)
2225
.unwrap ( )
2226
.set_modified ( old)
2227
.unwrap ( ) ;
4e88e774 Andy McCall 1 month ago ↶
2228
assert! ( repo.lock_snapshot ( ) .is_ok ( ) , " stale lock taken over" ) ;
2229
}
cdb5beba Andy McCall 1 month ago ↶
2230
2231
# [ test ]
2232
fn attributes_binary_eol_nocompress_lockable ( ) {
2233
hermetic ( ) ;
2234
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2235
let root = dir.path ( ) ;
2236
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
2237
write (
2238
root,
2239
" .oxyattributes" ,
2240
b " *.dat binary nocompress\n *.txt eol=crlf\n assets/** lockable\n " ,
2241
) ;
cdb5beba Andy McCall 1 month ago ↶
2242
write ( root, " plain.dat" , b " no nul bytes here\n " ) ;
2243
write ( root, " notes.txt" , b " one\r \n two\r \n " ) ;
2244
write ( root, " assets/model.bin" , b " \0 model" ) ;
2245
let h = repo.commit ( " attrs" , & [ ] ) .unwrap ( ) ;
2246
let snap = repo.load_state ( & [ h] ) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
2247
assert! (
2248
snap.files[ " plain.dat" ] .mode.opaque,
2249
" binary attribute forces opaque without NUL bytes"
2250
) ;
2251
assert! (
2252
matches! ( & snap.files[ " notes.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n two\n " ) ,
2253
" stored with LF"
2254
) ;
2255
assert! (
2256
! repo.snapshot ( " status" ) .unwrap ( ) .changed,
2257
" CRLF on disk equals LF in the state"
2258
) ;
cdb5beba Andy McCall 1 month ago ↶
2259
2260
let r = repo.gc ( true ) .unwrap ( ) ;
2261
assert! ( r.packed.objects > 0 ) ;
2262
2263
fs:: remove_file( root.join ( " notes.txt" ) ) .unwrap ( ) ;
2264
repo.checkout ( & [ h] , true , " co" ) .unwrap ( ) ;
2265
assert_eq! ( fs:: read( root.join ( " notes.txt" ) ) .unwrap ( ) , b " one\r \n two\r \n " ) ;
75167672 Andy McCall 1 month ago
2266
assert! ( fs:: metadata( root.join ( " assets/model.bin" ) )
2267
.unwrap ( )
2268
.permissions ( )
2269
.readonly ( ) ) ;
cdb5beba Andy McCall 1 month ago ↶
2270
assert! ( repo.lock_path ( " plain.dat" ) .is_err ( ) , " not lockable" ) ;
2271
repo.lock_path ( " assets/model.bin" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2272
assert! ( ! fs:: metadata( root.join ( " assets/model.bin" ) )
2273
.unwrap ( )
2274
.permissions ( )
2275
.readonly ( ) ) ;
cdb5beba Andy McCall 1 month ago ↶
2276
assert! ( repo.is_locked ( " assets/model.bin" ) ) ;
2277
write ( root, " assets/model.bin" , b " \0 model2" ) ;
2278
repo.commit ( " edit model" , & [ ] ) .unwrap ( ) ;
2279
repo.unlock_path ( " assets/model.bin" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2280
assert! ( fs:: metadata( root.join ( " assets/model.bin" ) )
2281
.unwrap ( )
2282
.permissions ( )
2283
.readonly ( ) ) ;
cdb5beba Andy McCall 1 month ago ↶
2284
2285
repo.checkout ( & [ h] , true , " co" ) .unwrap ( ) ;
2286
assert_eq! ( fs:: read( root.join ( " assets/model.bin" ) ) .unwrap ( ) , b " \0 model" ) ;
2287
}
6d3eefbc Andy McCall 1 month ago ↶
2288
2289
# [ cfg ( unix ) ]
2290
# [ test ]
2291
fn hooks_can_veto_observe_and_add_dependencies ( ) {
2292
use std:: os:: unix:: fs:: PermissionsExt;
2293
hermetic ( ) ;
2294
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2295
let root = dir.path ( ) ;
2296
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
2297
let hooks = root.join ( " .oxy/hooks" ) ;
2298
fs:: create_dir_all( & hooks) .unwrap ( ) ;
2299
let hook = | name : & str , body : & str | {
2300
let p = hooks.join ( name) ;
2301
fs:: write( & p, format! ( " #!/bin/sh\n {body} \n " ) ) .unwrap ( ) ;
2302
fs:: set_permissions( & p, fs:: Permissions:: from_mode( 0o755 ) ) .unwrap ( ) ;
2303
} ;
2304
hook ( " pre-commit" , " cat > \" $OXY_ROOT/.last-pre-commit\" ; grep -q WIP \" $OXY_ROOT/.last-pre-commit\" && { echo 'no WIP commits' >&2; exit 1; }; exit 0" ) ;
2305
hook ( " post-commit" , " cat > \" $OXY_ROOT/.last-post-commit\" " ) ;
2306
hook ( " post-checkout" , " cat > \" $OXY_ROOT/.last-post-checkout\" " ) ;
2307
hook ( " pre-push" , " echo pushing blocked >&2; exit 3" ) ;
2308
write ( root, " .oxyignore" , b " .last-*\n " ) ;
2309
write ( root, " a.txt" , b " a\n " ) ;
2310
let err = repo.commit ( " WIP: nope" , & [ ] ) .unwrap_err ( ) .to_string ( ) ;
2311
assert! ( err.contains ( " pre-commit refused: no WIP commits" ) , " {err}" ) ;
2312
let h1 = repo.commit ( " first" , & [ ] ) .unwrap ( ) ;
2313
let pre = fs:: read_to_string( root.join ( " .last-pre-commit" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2314
assert! (
2315
pre.contains ( " \" event\" :\" pre-commit\" " )
2316
& & pre.contains ( " \" path\" :\" a.txt\" " )
2317
& & pre.contains ( " \" message\" :\" first\" " ) ,
2318
" {pre}"
2319
) ;
6d3eefbc Andy McCall 1 month ago ↶
2320
let post = fs:: read_to_string( root.join ( " .last-post-commit" ) ) .unwrap ( ) ;
2321
assert! ( post.contains ( & h1.to_hex ( ) ) , " {post}" ) ;
2322
repo.checkout ( & [ h1] , false , " co" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2323
assert! ( fs:: read_to_string( root.join ( " .last-post-checkout" ) )
2324
.unwrap ( )
2325
.contains ( " post-checkout" ) ) ;
6d3eefbc Andy McCall 1 month ago ↶
2326
repo.config.set ( " remote.origin.url" , " /nowhere" ) .unwrap ( ) ;
2327
repo.save_config ( ) .unwrap ( ) ;
2328
let err = repo.push ( " origin" , false ) .unwrap_err ( ) .to_string ( ) ;
2329
assert! ( err.contains ( " pre-push refused: pushing blocked" ) , " {err}" ) ;
2330
2331
2332
write ( root, " b.txt" , b " b\n " ) ;
75167672 Andy McCall 1 month ago
2333
hook (
2334
" semantic-deps" ,
2335
& format! (
2336
" cat > \" $OXY_ROOT/.last-semantic\" ; echo '{{ \" deps\" : [\" {} \" ]}} '" ,
2337
h1.to_hex ( )
2338
) ,
2339
) ;
6d3eefbc Andy McCall 1 month ago ↶
2340
let h2 = repo.commit ( " b without hook" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2341
assert! (
2342
repo.store ( ) .get_change ( & h2) .unwrap ( ) .deps.is_empty ( ) ,
2343
" hook disabled by default"
2344
) ;
6d3eefbc Andy McCall 1 month ago ↶
2345
repo.config.set ( " hooks.semantic-deps" , " true" ) .unwrap ( ) ;
2346
repo.save_config ( ) .unwrap ( ) ;
2347
write ( root, " c.txt" , b " c\n " ) ;
2348
let h3 = repo.commit ( " c with hook" , & [ ] ) .unwrap ( ) ;
2349
assert_eq! ( repo.store ( ) .get_change ( & h3) .unwrap ( ) .deps, vec! [ h1] ) ;
2350
let sem = fs:: read_to_string( root.join ( " .last-semantic" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2351
assert! (
2352
sem.contains ( " \" touched_paths\" :[\" c.txt\" ]" ) & & sem.contains ( " candidate_deps" ) ,
2353
" {sem}"
2354
) ;
6d3eefbc Andy McCall 1 month ago ↶
2355
assert_eq! ( json_str ( " a\" b\\ c\n " ) , " \" a\\ \" b\\ \\ c\\ n\" " ) ;
2356
}
23b217d7 Andy McCall 1 month ago ↶
2357
2358
# [ test ]
2359
fn amend_into_older_change_and_split ( ) {
2360
hermetic ( ) ;
2361
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2362
let root = dir.path ( ) ;
2363
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
2364
repo.create_identity ( " Ada" , " a@b" , IdentityScope:: User)
2365
.unwrap ( ) ;
23b217d7 Andy McCall 1 month ago ↶
2366
repo.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
2367
write ( root, " a.txt" , b " one\n two\n " ) ;
2368
let h0 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
2369
write ( root, " a.txt" , b " one\n TWO\n " ) ;
2370
let h1 = repo.commit ( " edit two" , & [ ] ) .unwrap ( ) ;
2371
write ( root, " a.txt" , b " one\n TWO\n three\n " ) ;
2372
let h2 = repo.commit ( " add three" , & [ ] ) .unwrap ( ) ;
2373
let id1 = repo.store ( ) .get_change ( & h1) .unwrap ( ) .change_id;
2374
2375
2376
write ( root, " a.txt" , b " one\n TWO!\n three\n " ) ;
2377
let h1b = repo.amend_into ( & h1, None ) .unwrap ( ) ;
2378
assert_ne! ( h1b, h1) ;
2379
let c1b = repo.store ( ) .get_change ( & h1b) .unwrap ( ) ;
2380
assert_eq! ( c1b.change_id, id1) ;
2381
assert_eq! ( c1b.deps, vec! [ h0] ) ;
75167672 Andy McCall 1 month ago
2382
assert! ( matches! (
2383
repo.verify_change ( & h1b) .unwrap ( ) ,
2384
SignatureStatus:: Valid { .. }
2385
) ) ;
23b217d7 Andy McCall 1 month ago ↶
2386
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
2387
assert_eq! ( heads.len ( ) , 1 ) ;
2388
let top = repo.store ( ) .get_change ( & heads[ 0 ] ) .unwrap ( ) ;
2389
assert_eq! ( top.message, " add three" ) ;
75167672 Andy McCall 1 month ago
2390
assert_eq! (
2391
top.deps.iter ( ) .copied ( ) .collect:: < BTreeSet< _ > > ( ) ,
2392
[ h0, h1b] .into_iter ( ) .collect ( ) ,
2393
" depends on the file's creator and the rewritten line"
2394
) ;
23b217d7 Andy McCall 1 month ago ↶
2395
let snap = repo.load_state ( & heads) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
2396
assert! (
2397
matches! ( & snap.files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n TWO!\n three\n " )
2398
) ;
2399
assert! (
2400
! repo.snapshot ( " status" ) .unwrap ( ) .changed,
2401
" the working copy is clean after folding"
2402
) ;
23b217d7 Andy McCall 1 month ago ↶
2403
2404
let only = repo.load_state ( & [ h1b] ) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
2405
assert! (
2406
matches! ( & only.files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n TWO!\n " )
2407
) ;
23b217d7 Andy McCall 1 month ago ↶
2408
assert_eq! ( repo.log ( ) .unwrap ( ) .len ( ) , 4 ) ;
2409
2410
2411
write ( root, " a.txt" , b " one\n TWO!\n three!\n " ) ;
2412
let err = repo.amend_into ( & h1b, None ) .unwrap_err ( ) .to_string ( ) ;
2413
assert! ( err.contains ( " introduced after" ) , " {err}" ) ;
75167672 Andy McCall 1 month ago
2414
assert! (
2415
repo.snapshot ( " status" ) .unwrap ( ) .statuses.len ( ) = = 1 ,
2416
" nothing was changed by the refusal"
2417
) ;
23b217d7 Andy McCall 1 month ago ↶
2418
write ( root, " a.txt" , b " one\n TWO!\n three\n " ) ;
2419
2420
2421
write ( root, " b.txt" , b " b\n " ) ;
2422
write ( root, " c.txt" , b " c\n " ) ;
2423
let h3 = repo.commit ( " b and c" , & [ ] ) .unwrap ( ) ;
2424
let id3 = repo.store ( ) .get_change ( & h3) .unwrap ( ) .change_id;
2425
let ( p1, p2) = repo.split ( & h3, & [ " b.txt" .into ( ) ] ) .unwrap ( ) ;
2426
let c_1 = repo.store ( ) .get_change ( & p1) .unwrap ( ) ;
2427
let c_2 = repo.store ( ) .get_change ( & p2) .unwrap ( ) ;
2428
assert! ( c_1 .message.contains ( " part 1: b.txt" ) ) ;
2429
assert_eq! ( c_2 .change_id, id3) ;
2430
let heads: BTreeSet< _ > = repo.bookmark_heads ( ) .unwrap ( ) .into_iter ( ) .collect ( ) ;
75167672 Andy McCall 1 month ago
2431
assert! (
2432
heads.contains ( & p1) & & heads.contains ( & p2) ,
2433
" independent parts are both heads"
2434
) ;
2435
let snap = repo
2436
.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) )
2437
.unwrap ( )
2438
.materialize ( ) ;
23b217d7 Andy McCall 1 month ago ↶
2439
assert! ( snap.files.contains_key ( " b.txt" ) & & snap.files.contains_key ( " c.txt" ) ) ;
2440
let only1 = repo.load_state ( & [ p1] ) .unwrap ( ) .materialize ( ) ;
2441
assert! ( only1.files.contains_key ( " b.txt" ) & & ! only1.files.contains_key ( " c.txt" ) ) ;
2442
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
2443
assert! ( repo.split ( & p2, & [ " nope.txt" .into ( ) ] ) .is_err ( ) ) ;
2444
let _ = h2;
2445
}
28fc23d7 Andy McCall 1 month ago ↶
2446
2447
# [ test ]
2448
fn identity_lifecycle ( ) {
2449
hermetic ( ) ;
2450
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2451
let root = dir.path ( ) ;
2452
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
2453
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
2454
let mut a = Repo:: clone( & url, & root.join ( " a" ) )
2455
.unwrap ( )
2456
.with_home ( root.join ( " home-a" ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
2457
a.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2458
let ( key_a, _ ) = a
2459
.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
2460
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2461
write ( & root.join ( " a" ) , " a.txt" , b " a\n " ) ;
2462
let h1 = a.commit ( " one" , & [ ] ) .unwrap ( ) ;
2463
assert! ( a.policy_init ( ) .unwrap ( ) .applied) ;
2464
assert! ( a.push ( " origin" , false ) .unwrap ( ) .iter ( ) .all ( | r | r.ok ) ) ;
2465
2466
75167672 Andy McCall 1 month ago
2467
let mut e = Repo:: init( & root.join ( " e" ) )
2468
.unwrap ( )
2469
.with_home ( root.join ( " home-e" ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
2470
e.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2471
e.create_identity_with (
2472
" Eve" ,
2473
" [email protected] " ,
2474
IdentityScope:: User,
2475
Some ( Hlc:: now( ) .wall_ms - 1 ) ,
2476
)
2477
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2478
write ( & root.join ( " e" ) , " e.txt" , b " e\n " ) ;
2479
let he = e.commit ( " late" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2480
assert! (
2481
matches! ( e.verify_change ( & he) .unwrap ( ) , SignatureStatus:: Invalid( m) if m.contains ( " expired" ) )
2482
) ;
28fc23d7 Andy McCall 1 month ago ↶
2483
75167672 Andy McCall 1 month ago
2484
let r = e
2485
.renew_identity ( Some ( Hlc:: now( ) .wall_ms + 86_400_000 ) )
2486
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2487
assert_eq! ( e.identity ( & r.new) .unwrap ( ) .previous, Some ( r.old) ) ;
2488
write ( & root.join ( " e" ) , " e.txt" , b " e2\n " ) ;
2489
let he2 = e.commit ( " in time" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2490
assert! (
2491
matches! ( e.verify_change ( & he2) .unwrap ( ) , SignatureStatus:: Valid { key, .. } if key = = r.new)
2492
) ;
28fc23d7 Andy McCall 1 month ago ↶
2493
2494
2495
let r = a.rotate_identity ( ) .unwrap ( ) ;
2496
assert_eq! ( r.old, key_a) ;
2497
assert_eq! ( a.config.author_key, Some ( r.new) ) ;
2498
assert_eq! ( a.identity ( & r.new) .unwrap ( ) .previous, Some ( key_a) ) ;
2499
assert! ( r.policy.as_ref ( ) .unwrap ( ) .applied) ;
2500
let ( _ , sp) = a.current_policy ( ) .unwrap ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2501
assert_eq! (
2502
( sp.policy.serial, sp.policy.roots.clone ( ) ) ,
2503
( 2 , vec! [ r.new] )
2504
) ;
2505
assert! (
2506
! sp.policy.revoked.contains ( & key_a) & & sp.policy.authorizes ( & r.new, " main" ) ,
2507
" superseded, not revoked"
2508
) ;
28fc23d7 Andy McCall 1 month ago ↶
2509
assert! ( sp.policy.succeeds ( & r.new, & key_a) & & ! sp.policy.succeeds ( & key_a, & r.new) ) ;
75167672 Andy McCall 1 month ago
2510
assert! (
2511
matches! ( a.verify_change ( & h1) .unwrap ( ) , SignatureStatus:: Valid { key, revoked: false , .. } if key = = key_a)
2512
) ;
28fc23d7 Andy McCall 1 month ago ↶
2513
write ( & root.join ( " a" ) , " a.txt" , b " a2\n " ) ;
2514
let h2 = a.commit ( " two" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2515
assert! (
2516
matches! ( a.verify_change ( & h2) .unwrap ( ) , SignatureStatus:: Valid { key, revoked: false , .. } if key = = r.new)
2517
) ;
28fc23d7 Andy McCall 1 month ago ↶
2518
2519
let res = a.push ( " origin" , false ) .unwrap ( ) ;
2520
assert! ( res.iter ( ) .all ( | x | x.ok ) , " {res:?}" ) ;
2521
assert_eq! ( origin.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial, 2 ) ;
2522
let key_a = r.new;
2523
2524
2525
75167672 Andy McCall 1 month ago
2526
let mut b = Repo:: clone( & url, & root.join ( " b" ) )
2527
.unwrap ( )
2528
.with_home ( root.join ( " home-b" ) ) ;
2529
let ( key_b, id_b) = b
2530
.create_identity ( " Bob" , " [email protected] " , IdentityScope:: User)
2531
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2532
let r = a.delegate_add ( id_b.clone ( ) ) .unwrap ( ) ;
2533
assert_eq! ( r.old, key_a) ;
2534
let key_a2 = r.new;
2535
assert_eq! ( a.identity ( & key_a2) .unwrap ( ) .delegates, vec! [ key_b] ) ;
75167672 Andy McCall 1 month ago
2536
assert_eq! (
2537
a.identity ( & key_a2) .unwrap ( ) .ed25519_pk,
2538
a.identity ( & key_a) .unwrap ( ) .ed25519_pk
2539
) ;
28fc23d7 Andy McCall 1 month ago ↶
2540
assert! ( a.delegate_add ( id_b.clone ( ) ) .is_err ( ) , " already a delegate" ) ;
2541
assert! ( a.push ( " origin" , false ) .unwrap ( ) .iter ( ) .all ( | x | x.ok ) ) ;
2542
write ( & root.join ( " a" ) , " a.txt" , b " a3\n " ) ;
2543
let h3 = a.commit ( " three" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2544
assert! (
2545
matches! ( a.verify_change ( & h3) .unwrap ( ) , SignatureStatus:: Valid { key, .. } if key = = key_a2) ,
2546
" the same secret signs under the new KeyId"
2547
) ;
28fc23d7 Andy McCall 1 month ago ↶
2548
2549
a.set_user_config ( " core.sign" , " seal" ) .unwrap ( ) ;
2550
write ( & root.join ( " a" ) , " a.txt" , b " a4\n " ) ;
2551
let h4 = a.commit ( " four" , & [ ] ) .unwrap ( ) ;
2552
assert_eq! ( a.verify_change ( & h4) .unwrap ( ) , SignatureStatus:: Unsigned) ;
2553
let ( _ , sk_b) = b.secret_key ( ) .unwrap ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2554
a.store ( )
2555
.put_signature ( & h4, & sk_b.sign ( Domain:: Change, & key_b, & h4) )
2556
.unwrap ( ) ;
2557
assert! (
2558
matches! ( a.verify_change ( & h4) .unwrap ( ) , SignatureStatus:: Valid { key, .. } if key = = key_b)
2559
) ;
28fc23d7 Andy McCall 1 month ago ↶
2560
a.set_user_config ( " core.sign" , " change" ) .unwrap ( ) ;
2561
2562
let r = a.delegate_remove ( & key_b) .unwrap ( ) ;
2563
assert! ( a.identity ( & r.new) .unwrap ( ) .delegates.is_empty ( ) ) ;
2564
assert! ( a.delegate_remove ( & key_b) .is_err ( ) ) ;
2565
let key_a3 = r.new;
2566
let res = a.push ( " origin" , false ) .unwrap ( ) ;
2567
assert! ( res.iter ( ) .all ( | x | x.ok ) , " {res:?}" ) ;
2568
2569
2570
2571
assert! ( a.policy_root_add ( id_b.clone ( ) ) .unwrap ( ) .applied) ;
2572
assert! ( a.policy_root_add ( id_b.clone ( ) ) .is_err ( ) ) ;
2573
assert! ( a.policy_set_threshold ( 3 ) .is_err ( ) ) ;
2574
assert! ( a.policy_set_threshold ( 2 ) .unwrap ( ) .applied) ;
2575
assert! ( a.push ( " origin" , false ) .unwrap ( ) .iter ( ) .all ( | x | x.ok ) ) ;
2576
b.pull ( " origin" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2577
assert_eq! (
2578
b.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.root_threshold,
2579
2
2580
) ;
2581
let mut c = Repo:: init( & root.join ( " c" ) )
2582
.unwrap ( )
2583
.with_home ( root.join ( " home-c" ) ) ;
2584
let ( key_c, id_c) = c
2585
.create_identity ( " Cy" , " [email protected] " , IdentityScope:: User)
2586
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2587
let serial_before = a.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial;
2588
let proposal = a.policy_grant ( id_c, None ) .unwrap ( ) ;
2589
assert! ( ! proposal.applied & & proposal.sigs.len ( ) = = 1 ) ;
2590
assert_eq! ( proposal.policy.serial, serial_before + 1 ) ;
75167672 Andy McCall 1 month ago
2591
assert_eq! (
2592
a.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial,
2593
serial_before,
2594
" a proposal changes nothing yet"
2595
) ;
28fc23d7 Andy McCall 1 month ago ↶
2596
2597
let bytes = proposal.signed ( ) .encode ( ) ;
75167672 Andy McCall 1 month ago
2598
let signed = b
2599
.policy_sign ( SignedPolicy:: decode( & bytes) .unwrap ( ) )
2600
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2601
assert! ( signed.applied & & signed.sigs.len ( ) = = 2 ) ;
75167672 Andy McCall 1 month ago
2602
assert! ( b
2603
.current_policy ( )
2604
.unwrap ( )
2605
.unwrap ( )
2606
.1
2607
.policy
2608
.authorizes ( & key_c, " main" ) ) ;
2609
assert! (
2610
b.policy_sign ( signed.signed ( ) ) .is_err ( ) ,
2611
" a stale proposal is refused"
2612
) ;
28fc23d7 Andy McCall 1 month ago ↶
2613
75167672 Andy McCall 1 month ago
2614
assert! ( b
2615
.push ( " origin" , false )
2616
.unwrap ( )
2617
.iter ( )
2618
.any ( | x | x.name = = " policy" & & x.ok ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
2619
a.pull ( " origin" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2620
assert_eq! (
2621
a.current_policy ( ) .unwrap ( ) .unwrap ( ) .1. policy.serial,
2622
serial_before + 1
2623
) ;
28fc23d7 Andy McCall 1 month ago ↶
2624
2625
assert! ( a.policy_revoke ( & key_a3) .is_err ( ) ) ;
75167672 Andy McCall 1 month ago
2626
let u = a
2627
.revoke_identity ( & key_c, " left the project" )
2628
.unwrap ( )
2629
.unwrap ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
2630
assert! ( ! u.applied) ;
2631
assert! ( a.is_revoked ( & key_c) .unwrap ( ) ) ;
2632
assert! ( ! b.is_revoked ( & key_c) .unwrap ( ) ) ;
2633
let u = b.policy_sign ( u.signed ( ) ) .unwrap ( ) ;
2634
assert! ( u.applied & & u.policy.is_revoked ( & key_c) ) ;
2635
assert! ( b.is_revoked ( & key_c) .unwrap ( ) ) ;
2636
75167672 Andy McCall 1 month ago
2637
assert! (
2638
b.policy_revoke ( & key_a3) .is_err ( ) ,
2639
" would leave one root for a threshold of 2"
2640
) ;
28fc23d7 Andy McCall 1 month ago ↶
2641
2642
let key_c_own = c.config.author_key.unwrap ( ) ;
75167672 Andy McCall 1 month ago
2643
assert! ( c
2644
.revoke_identity ( & key_c_own, " lost laptop" )
2645
.unwrap ( )
2646
.is_none ( ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
2647
assert! ( c.is_revoked ( & key_c_own) .unwrap ( ) ) ;
2648
assert_eq! ( key_c_own, key_c) ;
2649
}
2650
6e723b81 Andy McCall 1 month ago ↶
2651
# [ test ]
2652
fn renames_are_detected_and_tracked_as_moves ( ) {
2653
hermetic ( ) ;
2654
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2655
let root = dir.path ( ) ;
2656
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
2657
write (
2658
root,
2659
" src/old.rs" ,
2660
b " fn a() {}\n fn b() {}\n fn c() {}\n fn d() {}\n " ,
2661
) ;
6e723b81 Andy McCall 1 month ago ↶
2662
write ( root, " img.bin" , b " \x89 PNG\0 \0 \x01 \x02 " ) ;
2663
write ( root, " tiny.txt" , b " x\n " ) ;
2664
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
2665
let node_old = repo.load_state ( & [ h1] ) .unwrap ( ) .materialize ( ) .files[ " src/old.rs" ] .node;
2666
let node_img = repo.load_state ( & [ h1] ) .unwrap ( ) .materialize ( ) .files[ " img.bin" ] .node;
2667
2668
2669
2670
fs:: remove_file( root.join ( " src/old.rs" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2671
write (
2672
root,
2673
" lib/new.rs" ,
2674
b " fn a() {}\n fn b() {}\n fn c() {}\n fn e() {}\n " ,
2675
) ;
6e723b81 Andy McCall 1 month ago ↶
2676
fs:: remove_file( root.join ( " img.bin" ) ) .unwrap ( ) ;
2677
write ( root, " assets/img.bin" , b " \x89 PNG\0 \0 \x01 \x02 " ) ;
2678
fs:: remove_file( root.join ( " tiny.txt" ) ) .unwrap ( ) ;
2679
write ( root, " other.txt" , b " completely different\n " ) ;
2680
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2681
let statuses: Vec< ( & str , ChangeStatus) > = snap
2682
.statuses
2683
.iter ( )
2684
.map ( | s | ( s.path.as_str ( ) , s.status) )
2685
.collect ( ) ;
6e723b81 Andy McCall 1 month ago ↶
2686
assert_eq! (
2687
statuses,
75167672 Andy McCall 1 month ago
2688
vec! [
2689
( " assets/img.bin" , ChangeStatus:: Renamed) ,
2690
( " lib/new.rs" , ChangeStatus:: Renamed) ,
2691
( " other.txt" , ChangeStatus:: Added) ,
2692
( " src" , ChangeStatus:: Removed) ,
2693
( " tiny.txt" , ChangeStatus:: Removed)
2694
]
6e723b81 Andy McCall 1 month ago ↶
2695
) ;
2696
let h2 = repo.commit ( " move things" , & [ ] ) .unwrap ( ) ;
2697
let c = repo.store ( ) .get_change ( & h2) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2698
let moves: Vec< _ > = c
2699
.tree_ops
2700
.iter ( )
2701
.filter ( | op | matches! ( op, TreeOp:: Move { .. } ) )
2702
.collect ( ) ;
6e723b81 Andy McCall 1 month ago ↶
2703
assert_eq! ( moves.len ( ) , 2 , " {:?}" , c.tree_ops) ;
75167672 Andy McCall 1 month ago
2704
assert! ( c.tree_ops.iter ( ) .any (
2705
| op | matches! ( op, TreeOp:: Move { node, name, .. } if * node = = node_old & & name = = " new.rs" )
2706
) ) ;
2707
assert! ( c.tree_ops.iter ( ) .any (
2708
| op | matches! ( op, TreeOp:: Move { node, name, .. } if * node = = node_img & & name = = " img.bin" )
2709
) ) ;
6e723b81 Andy McCall 1 month ago ↶
2710
let snap = repo.load_state ( & [ h2] ) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
2711
assert_eq! (
2712
snap.files[ " lib/new.rs" ] .node, node_old,
2713
" the node survives the rename"
2714
) ;
2715
assert! (
2716
matches! ( & snap.files[ " lib/new.rs" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " fn a() {}\n fn b() {}\n fn c() {}\n fn e() {}\n " )
2717
) ;
6e723b81 Andy McCall 1 month ago ↶
2718
assert! ( ! snap.files.contains_key ( " src/old.rs" ) & & ! snap.files.contains_key ( " src" ) ) ;
75167672 Andy McCall 1 month ago
2719
assert! ( render_change (
2720
& c,
2721
& repo.load_state ( & [ h1] ) .unwrap ( ) ,
2722
& repo.load_state ( & [ h1] ) .unwrap ( ) .materialize ( )
2723
)
2724
.contains ( " R " ) ) ;
6e723b81 Andy McCall 1 month ago ↶
2725
2726
let other = tempfile:: tempdir( ) .unwrap ( ) ;
2727
let clone = Repo:: clone( & root.to_string_lossy ( ) , other.path ( ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2728
assert_eq! (
2729
fs:: read( other.path ( ) .join ( " lib/new.rs" ) ) .unwrap ( ) ,
2730
b " fn a() {}\n fn b() {}\n fn c() {}\n fn e() {}\n "
2731
) ;
6e723b81 Andy McCall 1 month ago ↶
2732
assert! ( ! other.path ( ) .join ( " src" ) .exists ( ) ) ;
2733
assert! ( ! clone.snapshot ( " status" ) .unwrap ( ) .changed) ;
2734
2735
assert_eq! ( similarity ( b " a\n b\n " , b " a\n b\n " ) , 100 ) ;
2736
assert_eq! ( similarity ( b " a\n b\n c\n d\n " , b " a\n b\n x\n y\n " ) , 50 ) ;
2737
assert_eq! ( similarity ( b " hello\n " , b " world\n " ) , 0 ) ;
2738
assert_eq! ( similarity ( b " " , b " x\n " ) , 0 ) ;
2739
}
2740
30ea9c1e Andy McCall 1 month ago ↶
2741
# [ test ]
2742
fn git_fetch_and_push_round_trip ( ) {
2743
hermetic ( ) ;
75167672 Andy McCall 1 month ago
2744
if std:: process:: Command:: new( " git" )
2745
.arg ( " --version" )
2746
.output ( )
2747
.is_err ( )
2748
{
30ea9c1e Andy McCall 1 month ago ↶
2749
eprintln! ( " git not installed; skipping" ) ;
2750
return ;
2751
}
2752
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2753
let root = dir.path ( ) ;
2754
let git_in = | d : & Path, args : & [& str ]| -> String {
2755
let out = std:: process:: Command:: new( " git" )
2756
.arg ( " -C" )
2757
.arg ( d)
75167672 Andy McCall 1 month ago
2758
.args ( [
2759
" -c" ,
2760
" user.name=Grace" ,
2761
" -c" ,
2762
" [email protected] " ,
2763
" -c" ,
2764
" commit.gpgsign=false" ,
2765
] )
30ea9c1e Andy McCall 1 month ago ↶
2766
.args ( args)
2767
.output ( )
2768
.unwrap ( ) ;
75167672 Andy McCall 1 month ago
2769
assert! (
2770
out.status.success ( ) ,
2771
" git {args:?}: {}" ,
2772
String :: from_utf8_lossy( & out.stderr)
2773
) ;
30ea9c1e Andy McCall 1 month ago ↶
2774
String :: from_utf8_lossy( & out.stdout) .trim ( ) .to_string ( )
2775
} ;
2776
2777
let remote = root.join ( " remote.git" ) ;
2778
fs:: create_dir_all( & remote) .unwrap ( ) ;
2779
git_in ( & remote, & [ " init" , " -q" , " --bare" , " -b" , " main" ] ) ;
2780
let work = root.join ( " work" ) ;
75167672 Andy McCall 1 month ago
2781
git_in (
2782
root,
2783
& [
2784
" clone" ,
2785
" -q" ,
2786
remote.to_str ( ) .unwrap ( ) ,
2787
work.to_str ( ) .unwrap ( ) ,
2788
] ,
2789
) ;
30ea9c1e Andy McCall 1 month ago ↶
2790
git_in ( & work, & [ " checkout" , " -q" , " -b" , " main" ] ) ;
2791
write ( & work, " README.md" , b " # Project\n " ) ;
2792
write ( & work, " src/lib.rs" , b " pub fn a() {}\n " ) ;
2793
git_in ( & work, & [ " add" , " ." ] ) ;
2794
git_in ( & work, & [ " commit" , " -q" , " -m" , " initial" ] ) ;
2795
write ( & work, " src/lib.rs" , b " pub fn a() {}\n pub fn b() {}\n " ) ;
2796
git_in ( & work, & [ " commit" , " -q" , " -am" , " second" ] ) ;
2797
git_in ( & work, & [ " push" , " -q" , " origin" , " main" ] ) ;
2798
let tip1 = git_in ( & work, & [ " rev-parse" , " HEAD" ] ) ;
2799
2800
2801
let o = root.join ( " oxy" ) ;
2802
let mut repo = Repo:: init( & o) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
2803
repo.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
2804
.unwrap ( ) ;
30ea9c1e Andy McCall 1 month ago ↶
2805
let url = remote.to_string_lossy ( ) .to_string ( ) ;
2806
let r = repo.git_fetch ( & url, " main" ) .unwrap ( ) ;
2807
assert_eq! ( ( r.commits, r.heads) , ( 2 , 1 ) ) ;
75167672 Andy McCall 1 month ago
2808
assert_eq! (
2809
fs:: read( o.join ( " src/lib.rs" ) ) .unwrap ( ) ,
2810
b " pub fn a() {}\n pub fn b() {}\n "
2811
) ;
30ea9c1e Andy McCall 1 month ago ↶
2812
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
75167672 Andy McCall 1 month ago
2813
assert_eq! (
2814
repo.git_fetch ( & url, " main" ) .unwrap ( ) .commits,
2815
0 ,
2816
" nothing new"
2817
) ;
30ea9c1e Andy McCall 1 month ago ↶
2818
2819
75167672 Andy McCall 1 month ago
2820
write (
2821
& o,
2822
" src/lib.rs" ,
2823
b " pub fn a() {}\n pub fn b() {}\n pub fn c() {}\n " ,
2824
) ;
30ea9c1e Andy McCall 1 month ago ↶
2825
write ( & o, " NOTES.md" , b " from oxy\n " ) ;
2826
let h_oxy = repo.commit ( " add c and notes" , & [ ] ) .unwrap ( ) ;
2827
let r = repo.git_push ( & url, " main" ) .unwrap ( ) ;
2828
assert_eq! ( r.exported, 1 , " {r:?}" ) ;
2829
git_in ( & work, & [ " pull" , " -q" , " --ff-only" , " origin" , " main" ] ) ;
75167672 Andy McCall 1 month ago
2830
assert_eq! (
2831
git_in ( & work, & [ " log" , " --format=%s" , " main" ] ) ,
2832
" add c and notes\n second\n initial"
2833
) ;
30ea9c1e Andy McCall 1 month ago ↶
2834
assert_eq! ( git_in ( & work, & [ " rev-parse" , " HEAD~1" ] ) , tip1) ;
2835
assert_eq! ( fs:: read( work.join ( " NOTES.md" ) ) .unwrap ( ) , b " from oxy\n " ) ;
2836
assert_eq! ( git_in ( & work, & [ " log" , " -1" , " --format=%an" , " main" ] ) , " Ada" ) ;
75167672 Andy McCall 1 month ago
2837
assert! ( git_in ( & work, & [ " log" , " -1" , " --format=%B" , " main" ] )
2838
.contains ( & format! ( " Oxy-Hash: {} " , h_oxy.to_hex ( ) ) ) ) ;
2839
assert_eq! (
2840
repo.git_push ( & url, " main" ) .unwrap ( ) .exported,
2841
0 ,
2842
" already pushed"
2843
) ;
30ea9c1e Andy McCall 1 month ago ↶
2844
2845
2846
2847
write ( & work, " README.md" , b " # Project\n \n More.\n " ) ;
2848
git_in ( & work, & [ " commit" , " -q" , " -am" , " readme from git" ] ) ;
2849
git_in ( & work, & [ " push" , " -q" , " origin" , " main" ] ) ;
2850
let tip2 = git_in ( & work, & [ " rev-parse" , " HEAD" ] ) ;
75167672 Andy McCall 1 month ago
2851
assert! ( repo
2852
.git_push ( & url, " main" )
2853
.unwrap_err ( )
2854
.to_string ( )
2855
.contains ( " fetch" ) ) ;
30ea9c1e Andy McCall 1 month ago ↶
2856
let r = repo.git_fetch ( & url, " main" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2857
assert_eq! (
2858
( r.commits, r.heads) ,
2859
( 1 , 2 ) ,
2860
" the Git change and the oxy change commute"
2861
) ;
2862
assert_eq! (
2863
fs:: read( o.join ( " README.md" ) ) .unwrap ( ) ,
2864
b " # Project\n \n More.\n "
2865
) ;
30ea9c1e Andy McCall 1 month ago ↶
2866
assert_eq! ( fs:: read( o.join ( " NOTES.md" ) ) .unwrap ( ) , b " from oxy\n " ) ;
2867
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
2868
2869
write ( & o, " NOTES.md" , b " from oxy\n again\n " ) ;
2870
repo.commit ( " notes again" , & [ ] ) .unwrap ( ) ;
2871
assert_eq! ( repo.git_push ( & url, " main" ) .unwrap ( ) .exported, 1 ) ;
2872
git_in ( & work, & [ " pull" , " -q" , " --ff-only" , " origin" , " main" ] ) ;
2873
assert_eq! ( git_in ( & work, & [ " rev-parse" , " HEAD~1" ] ) , tip2) ;
75167672 Andy McCall 1 month ago
2874
assert_eq! (
2875
fs:: read( work.join ( " NOTES.md" ) ) .unwrap ( ) ,
2876
b " from oxy\n again\n "
2877
) ;
2878
assert_eq! (
2879
git_in ( & work, & [ " log" , " --format=%s" , " main" ] )
2880
.lines ( )
2881
.count ( ) ,
2882
5
2883
) ;
30ea9c1e Andy McCall 1 month ago ↶
2884
}
2885
2b872d07 Andy McCall 1 month ago ↶
2886
2887
2888
# [ test ]
2889
# [ ignore ]
2890
fn bench_one_gibibyte_file ( ) {
2891
hermetic ( ) ;
2892
2893
let dir = match std:: env:: var_os( " OXY_BENCH_DIR" ) {
2894
Some ( d) => tempfile:: tempdir_in( d) .unwrap ( ) ,
2895
None => tempfile:: tempdir( ) .unwrap ( ) ,
2896
} ;
2897
let root = dir.path ( ) ;
75167672 Andy McCall 1 month ago
2898
let repo = Repo:: init( & root.join ( " r" ) )
2899
.unwrap ( )
2900
.with_home ( root.join ( " home" ) ) ;
2b872d07 Andy McCall 1 month ago ↶
2901
let size = 1 usize < < 30 ;
2902
let mut data = vec! [ 0 u8 ; size] ;
2903
let mut x = 0x9e3779b97f4a7c15 u64 ;
2904
for chunk in data.chunks_mut ( 8 ) {
2905
x ^ = x < < 13 ;
2906
x ^ = x > > 7 ;
2907
x ^ = x < < 17 ;
2908
let b = x.to_le_bytes ( ) ;
2909
chunk.copy_from_slice ( & b[ .. chunk.len ( ) ] ) ;
2910
}
2911
let t = std:: time:: Instant:: now( ) ;
2912
write ( & root.join ( " r" ) , " big.bin" , & data) ;
2913
eprintln! ( " write 1 GiB to disk: {:.2?}" , t.elapsed ( ) ) ;
2914
let t = std:: time:: Instant:: now( ) ;
2915
let h = repo.commit ( " big" , & [ ] ) .unwrap ( ) ;
2916
eprintln! ( " commit (chunk + hash + store): {:.2?}" , t.elapsed ( ) ) ;
2917
let t = std:: time:: Instant:: now( ) ;
2918
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
2919
eprintln! ( " status on a clean 1 GiB working copy: {:.2?}" , t.elapsed ( ) ) ;
2920
let t = std:: time:: Instant:: now( ) ;
2921
let objects = repo.store ( ) .list ( ) .unwrap ( ) .len ( ) ;
2922
eprintln! ( " objects stored: {objects} ({:.2?} to list)" , t.elapsed ( ) ) ;
2923
let t = std:: time:: Instant:: now( ) ;
2924
let g = repo.gc ( true ) .unwrap ( ) ;
2925
eprintln! ( " gc (pack): {:.2?} {g:?}" , t.elapsed ( ) ) ;
2926
let t = std:: time:: Instant:: now( ) ;
2927
let clone = Repo:: clone( & root.join ( " r" ) .to_string_lossy ( ) , & root.join ( " c" ) ) .unwrap ( ) ;
2928
eprintln! ( " clone (fetch + checkout): {:.2?}" , t.elapsed ( ) ) ;
2929
let t = std:: time:: Instant:: now( ) ;
2930
let back = fs:: read( root.join ( " c/big.bin" ) ) .unwrap ( ) ;
2931
assert_eq! ( blake3:: hash( & back) , blake3:: hash( & data) ) ;
2932
eprintln! ( " read back and verify: {:.2?}" , t.elapsed ( ) ) ;
2933
assert_eq! ( clone.bookmark_heads ( ) .unwrap ( ) , vec! [ h] ) ;
2934
let store_bytes: u64 = walkdir_size ( & root.join ( " r/.oxy/objects" ) ) ;
75167672 Andy McCall 1 month ago
2935
eprintln! (
2936
" store size: {:.1} MiB for 1 GiB of data" ,
2937
store_bytes as f64 / ( 1024. 0 * 1024. 0 )
2938
) ;
2b872d07 Andy McCall 1 month ago ↶
2939
}
2940
2941
fn walkdir_size ( p : & Path) -> u64 {
2942
let mut total = 0 ;
2943
if let Ok ( rd) = fs:: read_dir( p) {
2944
for e in rd.flatten ( ) {
2945
let path = e.path ( ) ;
2946
if path.is_dir ( ) {
2947
total + = walkdir_size ( & path) ;
2948
} else if let Ok ( m) = fs:: metadata( & path) {
2949
total + = m.len ( ) ;
2950
}
2951
}
2952
}
2953
total
2954
}
2955
b7f102fc Andy McCall 1 month ago ↶
2956
2957
2958
# [ cfg ( unix ) ]
2959
# [ test ]
2960
fn stat_cache_skips_reading_unchanged_opaque_files ( ) {
2961
use std:: os:: unix:: fs:: PermissionsExt;
2962
hermetic ( ) ;
2963
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2964
let root = dir.path ( ) ;
2965
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
2966
let mut big = vec! [ 0 u8 ; 300 * 1024 ] ;
2967
for ( i, b) in big.iter_mut ( ) .enumerate ( ) {
2968
* b = ( i as u32 ) .wrapping_mul ( 2654435761 ) .to_le_bytes ( ) [ 1 ] ;
2969
}
2970
big[ 0 ] = 0 ;
2971
write ( root, " big.bin" , & big) ;
2972
write ( root, " a.txt" , b " text\n " ) ;
2973
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
2974
let ( _ , _ , m) = repo.read_manifest ( ) .unwrap ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2975
assert! (
2976
m.entries[ " big.bin" ] .blob.is_some ( ) ,
2977
" opaque files record their blob"
2978
) ;
b7f102fc Andy McCall 1 month ago ↶
2979
assert! ( m.entries[ " a.txt" ] .blob.is_none ( ) ) ;
2980
let blob = m.entries[ " big.bin" ] .blob.unwrap ( ) ;
2981
assert! ( repo.store ( ) .has ( & blob) ) ;
2982
2983
2984
2985
let old = std:: time:: SystemTime:: now( ) - std:: time:: Duration:: from_secs( 30 ) ;
75167672 Andy McCall 1 month ago
2986
fs:: File:: options( )
2987
.write ( true )
2988
.open ( root.join ( " big.bin" ) )
2989
.unwrap ( )
2990
.set_modified ( old)
2991
.unwrap ( ) ;
b7f102fc Andy McCall 1 month ago ↶
2992
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
2993
2994
fs:: set_permissions( root.join ( " big.bin" ) , fs:: Permissions:: from_mode( 0o000 ) ) .unwrap ( ) ;
2995
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2996
assert! (
2997
! snap.changed & & snap.statuses.is_empty ( ) ,
2998
" {:?}" ,
2999
snap.statuses
3000
) ;
b7f102fc Andy McCall 1 month ago ↶
3001
fs:: set_permissions( root.join ( " big.bin" ) , fs:: Permissions:: from_mode( 0o644 ) ) .unwrap ( ) ;
3002
3003
let mut big2 = big.clone ( ) ;
3004
big2[ 10 ] ^ = 0xff ;
3005
write ( root, " big.bin" , & big2) ;
3006
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3007
assert_eq! (
3008
snap.statuses
3009
.iter ( )
3010
.map ( | s | ( s.path.as_str ( ) , s.status) )
3011
.collect:: < Vec< _ > > ( ) ,
3012
vec! [ ( " big.bin" , ChangeStatus:: Modified) ]
3013
) ;
b7f102fc Andy McCall 1 month ago ↶
3014
let h2 = repo.commit ( " edit" , & [ ] ) .unwrap ( ) ;
3015
let files = repo.load_state ( & [ h2] ) .unwrap ( ) .materialize ( ) .files;
75167672 Andy McCall 1 month ago
3016
assert_eq! (
3017
files[ " big.bin" ] .content,
3018
FileContent:: Opaque {
3019
blobs: vec! [ Repo:: blob_ref( & big2) ]
3020
}
3021
) ;
b7f102fc Andy McCall 1 month ago ↶
3022
3023
repo.checkout ( & [ h1] , false , " checkout" ) .unwrap ( ) ;
3024
let ( _ , _ , m) = repo.read_manifest ( ) .unwrap ( ) .unwrap ( ) ;
3025
assert_eq! ( m.entries[ " big.bin" ] .blob, Some ( Repo:: blob_ref( & big) ) ) ;
3026
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3027
}
3028
50a046e4 Andy McCall 1 month ago ↶
3029
# [ test ]
3030
fn status_json_lists_conflicts_with_contributors ( ) {
3031
hermetic ( ) ;
3032
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3033
let root = dir.path ( ) ;
3034
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3035
write ( root, " a.txt" , b " one\n " ) ;
3036
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
3037
write ( root, " a.txt" , b " one\n left\n " ) ;
3038
let ha = repo.commit ( " left" , & [ ] ) .unwrap ( ) ;
3039
repo.checkout ( & [ h1] , true , " checkout" ) .unwrap ( ) ;
3040
write ( root, " a.txt" , b " one\n right\n " ) ;
3041
let hb = repo.commit ( " right" , & [ ] ) .unwrap ( ) ;
3042
repo.checkout ( & [ ha, hb] , true , " checkout" ) .unwrap ( ) ;
3043
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
3044
assert! ( snap.base_snapshot.is_conflicted ( ) ) ;
3045
let json = repo.status_json ( & snap) .unwrap ( ) ;
3046
let ida = repo.store ( ) .get_change ( & ha) .unwrap ( ) .change_id.to_hex ( ) ;
3047
let idb = repo.store ( ) .get_change ( & hb) .unwrap ( ) .change_id.to_hex ( ) ;
3048
assert! ( json.contains ( " \" conflicted\" :true" ) , " {json}" ) ;
75167672 Andy McCall 1 month ago
3049
assert! (
3050
json.contains ( " \" kind\" :\" text\" ,\" path\" :\" a.txt\" " ) ,
3051
" {json}"
3052
) ;
50a046e4 Andy McCall 1 month ago ↶
3053
assert! ( json.contains ( & ida) & & json.contains ( & idb) , " {json}" ) ;
3054
assert! ( json.contains ( " \" resolved\" :false" ) , " {json}" ) ;
3055
assert! ( json.starts_with ( ' {' ) & & json.ends_with ( ' }' ) ) ;
3056
3057
write ( root, " a.txt" , b " one\n left\n right\n " ) ;
3058
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
3059
let json = repo.status_json ( & snap) .unwrap ( ) ;
3060
assert! ( json.contains ( " \" resolved\" :true" ) , " {json}" ) ;
3061
repo.commit ( " resolve" , & [ ] ) .unwrap ( ) ;
3062
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
3063
let json = repo.status_json ( & snap) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3064
assert! (
3065
json.contains ( " \" clean\" :true,\" conflicted\" :false,\" paths\" :[],\" conflicts\" :[]" ) ,
3066
" {json}"
3067
) ;
50a046e4 Andy McCall 1 month ago ↶
3068
}
3069
9ac1fbe2 Andy McCall 1 month ago ↶
3070
# [ test ]
3071
fn ignore_rules_do_not_untrack_files ( ) {
3072
hermetic ( ) ;
3073
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3074
let root = dir.path ( ) ;
3075
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3076
write ( root, " src.c" , b " int x;\n " ) ;
3077
write ( root, " build/out.o" , b " \0 obj" ) ;
3078
write ( root, " sub/keep.log" , b " keep\n " ) ;
3079
write ( root, " sub/junk.log" , b " junk\n " ) ;
3080
write ( root, " .oxyignore" , b " /build\n *.log\n !sub/keep.log\n " ) ;
3081
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
3082
let added: Vec< & str > = snap.statuses.iter ( ) .map ( | s | s.path.as_str ( ) ) .collect ( ) ;
75167672 Andy McCall 1 month ago
3083
assert_eq! (
3084
added,
3085
vec! [ " .oxyignore" , " src.c" , " sub/keep.log" ] ,
3086
" anchored, glob, and negated rules"
3087
) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3088
let h1 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
3089
3090
write ( root, " .oxyignore" , b " /build\n *.log\n !sub/keep.log\n src.c\n " ) ;
3091
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3092
assert_eq! (
3093
snap.statuses
3094
.iter ( )
3095
.map ( | s | s.path.as_str ( ) )
3096
.collect:: < Vec< _ > > ( ) ,
3097
vec! [ " .oxyignore" ]
3098
) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3099
let h2 = repo.commit ( " ignore src.c" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3100
assert! ( repo
3101
.load_state ( & [ h2] )
3102
.unwrap ( )
3103
.materialize ( )
3104
.files
3105
.contains_key ( " src.c" ) ) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3106
3107
assert_eq! ( repo.quick_dirty ( ) .unwrap ( ) , Some ( false ) ) ;
3108
write ( root, " src.c" , b " int x, y;\n " ) ;
3109
assert_eq! ( repo.quick_dirty ( ) .unwrap ( ) , Some ( true ) ) ;
3110
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3111
assert_eq! (
3112
snap.statuses
3113
.iter ( )
3114
.map ( | s | ( s.path.as_str ( ) , s.status) )
3115
.collect:: < Vec< _ > > ( ) ,
3116
vec! [ ( " src.c" , ChangeStatus:: Modified) ]
3117
) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3118
let h3 = repo.commit ( " edit src.c" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3119
assert! (
3120
matches! ( & repo.load_state ( & [ h3] ) .unwrap ( ) .materialize ( ) .files[ " src.c" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " int x, y;\n " )
3121
) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3122
3123
fs:: remove_file( root.join ( " src.c" ) ) .unwrap ( ) ;
3124
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3125
assert_eq! (
3126
snap.statuses
3127
.iter ( )
3128
.map ( | s | ( s.path.as_str ( ) , s.status) )
3129
.collect:: < Vec< _ > > ( ) ,
3130
vec! [ ( " src.c" , ChangeStatus:: Removed) ]
3131
) ;
9ac1fbe2 Andy McCall 1 month ago ↶
3132
repo.checkout ( & [ h3] , true , " checkout" ) .unwrap ( ) ;
3133
assert_eq! ( fs:: read( root.join ( " src.c" ) ) .unwrap ( ) , b " int x, y;\n " ) ;
3134
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3135
let _ = h1;
3136
}
3137
b837d2b5 Andy McCall 1 month ago ↶
3138
# [ test ]
3139
fn split_works_on_non_heads_by_node ( ) {
3140
hermetic ( ) ;
3141
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3142
let root = dir.path ( ) ;
3143
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3144
write ( root, " a.txt" , b " one\n " ) ;
3145
write ( root, " junk/x.lock" , b " lock\n " ) ;
3146
let h1 = repo.commit ( " work plus junk" , & [ ] ) .unwrap ( ) ;
3147
write ( root, " a.txt" , b " one\n two\n " ) ;
3148
let h2 = repo.commit ( " more work" , & [ ] ) .unwrap ( ) ;
3149
assert_eq! ( repo.store ( ) .get_change ( & h2) .unwrap ( ) .deps, vec! [ h1] ) ;
3150
let id1 = repo.store ( ) .get_change ( & h1) .unwrap ( ) .change_id;
3151
3152
let ( hj, h1b) = repo.split ( & h1, & [ " junk" .into ( ) ] ) .unwrap ( ) ;
3153
assert_ne! ( h1b, h1) ;
75167672 Andy McCall 1 month ago
3154
assert_eq! (
3155
repo.store ( ) .get_change ( & h1b) .unwrap ( ) .change_id,
3156
id1,
3157
" the rest keeps the ChangeId"
3158
) ;
b837d2b5 Andy McCall 1 month ago ↶
3159
let junk = repo.store ( ) .get_change ( & hj) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3160
assert! ( junk.tree_ops.iter ( ) .all (
3161
| op | matches! ( op, TreeOp:: AddNode { name, .. } if name = = " junk" | | name = = " x.lock" )
3162
) ) ;
b837d2b5 Andy McCall 1 month ago ↶
3163
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3164
assert! (
3165
heads.contains ( & hj) ,
3166
" the split-off part is a head of its own"
3167
) ;
b837d2b5 Andy McCall 1 month ago ↶
3168
let snap = repo.load_state ( & heads) .unwrap ( ) .materialize ( ) ;
75167672 Andy McCall 1 month ago
3169
assert! (
3170
matches! ( & snap.files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " one\n two\n " ) ,
3171
" the dependent was rewritten onto the new hash"
3172
) ;
b837d2b5 Andy McCall 1 month ago ↶
3173
assert! ( snap.files.contains_key ( " junk/x.lock" ) ) ;
75167672 Andy McCall 1 month ago
3174
let h2b = heads
3175
.iter ( )
3176
.find ( | h | repo.store ( ) .get_change ( h) .unwrap ( ) .message = = " more work" )
3177
.copied ( )
3178
.unwrap ( ) ;
b837d2b5 Andy McCall 1 month ago ↶
3179
assert_eq! ( repo.store ( ) .get_change ( & h2b) .unwrap ( ) .deps, vec! [ h1b] ) ;
3180
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3181
3182
repo.abandon ( & hj) .unwrap ( ) ;
3183
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
3184
let snap = repo.load_state ( & heads) .unwrap ( ) .materialize ( ) ;
3185
assert! ( ! snap.files.contains_key ( " junk/x.lock" ) & & ! snap.files.contains_key ( " junk" ) ) ;
3186
assert! ( snap.files.contains_key ( " a.txt" ) ) ;
3187
3188
write ( root, " junk/y.lock" , b " y\n " ) ;
3189
let hb = repo.commit ( " junk base" , & [ ] ) .unwrap ( ) ;
3190
write ( root, " junk/y.lock" , b " yy\n " ) ;
3191
write ( root, " b.txt" , b " b\n " ) ;
3192
repo.commit ( " touches junk" , & [ ] ) .unwrap ( ) ;
3193
let err = repo.split ( & hb, & [ " junk" .into ( ) ] ) .unwrap_err ( ) .to_string ( ) ;
3194
assert! ( err.contains ( " depends on the split-off paths" ) , " {err}" ) ;
3195
}
3196
7b9809e6 Andy McCall 1 month ago ↶
3197
# [ test ]
3198
fn bookmarks_switch_merge_and_push ( ) {
3199
hermetic ( ) ;
3200
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3201
let root = dir.path ( ) ;
3202
let origin = Repo:: init( & root.join ( " origin" ) ) .unwrap ( ) ;
3203
let url = root.join ( " origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
3204
let mut repo = Repo:: clone( & url, & root.join ( " r" ) )
3205
.unwrap ( )
3206
.with_home ( root.join ( " home" ) ) ;
7b9809e6 Andy McCall 1 month ago ↶
3207
let r = root.join ( " r" ) ;
3208
write ( & r, " a.txt" , b " a\n " ) ;
3209
let h_a = repo.commit ( " a" , & [ ] ) .unwrap ( ) ;
3210
assert! ( repo.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
3211
3212
3213
assert! ( repo.bookmark_create ( " bad name.." , None ) .is_err ( ) ) ;
3214
assert! ( repo.bookmark_create ( " -x" , None ) .is_err ( ) ) ;
3215
assert_eq! ( repo.bookmark_create ( " feature" , None ) .unwrap ( ) , vec! [ h_a] ) ;
3216
assert! ( repo.bookmark_create ( " feature" , None ) .is_err ( ) , " exists" ) ;
3217
repo.bookmark_switch ( " feature" , false ) .unwrap ( ) ;
3218
assert_eq! ( repo.config.bookmark, " feature" ) ;
3219
write ( & r, " a.txt" , b " a\n b\n " ) ;
3220
write ( & r, " b.txt" , b " b\n " ) ;
3221
let h_b = repo.commit ( " b on feature" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3222
assert_eq! (
3223
repo.bookmark_heads ( ) .unwrap ( ) ,
3224
vec! [ h_b] ,
3225
" b edits a.txt, so it depends on a"
3226
) ;
3227
assert_eq! (
3228
repo.resolve_heads ( " main" ) .unwrap ( ) ,
3229
vec! [ h_a] ,
3230
" main is untouched"
3231
) ;
3232
assert_eq! (
3233
repo.resolve_change ( " feature" ) .unwrap ( ) ,
3234
h_b,
3235
" a one-head bookmark names its change"
3236
) ;
7b9809e6 Andy McCall 1 month ago ↶
3237
3238
let reopened = Repo:: open( & r) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3239
assert_eq! ( reopened.config.bookmark, " feature" ) ;
3240
3241
repo.bookmark_switch ( " main" , false ) .unwrap ( ) ;
3242
assert! ( ! r.join ( " b.txt" ) .exists ( ) ) ;
3243
assert_eq! ( fs:: read( r.join ( " a.txt" ) ) .unwrap ( ) , b " a\n " ) ;
3244
write ( & r, " c.txt" , b " c\n " ) ;
3245
let h_c = repo.commit ( " c on main" , & [ ] ) .unwrap ( ) ;
3246
let list = repo.bookmarks ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3247
assert_eq! (
3248
list.iter ( )
3249
.map ( | b | ( b.name.as_str ( ) , b.current) )
3250
.collect:: < Vec< _ > > ( ) ,
3251
vec! [ ( " feature" , false ) , ( " main" , true ) ]
3252
) ;
3253
assert_eq! (
3254
list.iter ( ) .find ( | b | b.name = = " main" ) .unwrap ( ) .ahead,
3255
Some ( 1 ) ,
3256
" c is not on origin yet"
3257
) ;
7b9809e6 Andy McCall 1 month ago ↶
3258
3259
let m = repo.merge ( " feature" ) .unwrap ( ) ;
3260
assert! ( m.changed & & m.checked_out) ;
3261
let mut want = vec! [ h_b, h_c] ;
3262
want.sort ( ) ;
3263
assert_eq! ( m.heads, want) ;
3264
assert_eq! ( fs:: read( r.join ( " b.txt" ) ) .unwrap ( ) , b " b\n " ) ;
3265
assert! ( ! repo.merge ( " feature" ) .unwrap ( ) .changed, " already merged" ) ;
3266
assert! ( repo.merge ( " main" ) .is_err ( ) ) ;
3267
3268
assert! ( repo.push_bookmark ( " origin" , " feature" , false ) .unwrap ( ) [ 0 ] .ok) ;
3269
assert_eq! ( origin.resolve_heads ( " feature" ) .unwrap ( ) , vec! [ h_b] ) ;
3270
assert! ( repo.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
75167672 Andy McCall 1 month ago
3271
assert_eq! (
3272
repo.bookmarks ( )
3273
.unwrap ( )
3274
.iter ( )
3275
.find ( | b | b.name = = " main" )
3276
.unwrap ( )
3277
.ahead,
3278
Some ( 0 )
3279
) ;
7b9809e6 Andy McCall 1 month ago ↶
3280
3281
let ws = repo.workspace_add ( & root.join ( " ws" ) , None ) .unwrap ( ) ;
3282
assert_eq! ( ws.config.bookmark, " main" ) ;
3283
let mut ws = ws;
3284
ws.bookmark_switch ( " feature" , false ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3285
assert_eq! (
3286
Repo:: open( & r) .unwrap ( ) .config.bookmark,
3287
" main" ,
3288
" the first workspace is unaffected"
3289
) ;
3290
assert! (
3291
repo.bookmark_delete ( " feature" ) .is_err ( ) ,
3292
" current bookmark of another workspace"
3293
) ;
7b9809e6 Andy McCall 1 month ago ↶
3294
ws.bookmark_switch ( " main" , false ) .unwrap ( ) ;
3295
3296
assert_eq! ( repo.bookmark_set ( " feature" , & [ h_a] ) .unwrap ( ) , vec! [ h_a] ) ;
3297
assert! ( repo.bookmark_delete ( " main" ) .is_err ( ) , " current here" ) ;
3298
repo.bookmark_delete ( " feature" ) .unwrap ( ) ;
3299
assert! ( repo.resolve_heads ( " feature" ) .is_err ( ) ) ;
3300
assert! ( repo.bookmark_set ( " feature" , & [ h_a] ) .is_err ( ) ) ;
75167672 Andy McCall 1 month ago
3301
assert! (
3302
repo.checkout ( & [ h_a] , false , " checkout" ) .is_ok ( ) ,
3303
" changes of a deleted bookmark remain"
3304
) ;
7b9809e6 Andy McCall 1 month ago ↶
3305
}
3306
4dee277c Andy McCall 1 month ago ↶
3307
# [ test ]
3308
fn oxygen_subrepos_pin_follow_and_update ( ) {
3309
hermetic ( ) ;
3310
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3311
let root = dir.path ( ) ;
3312
3313
let lib_origin = Repo:: init( & root.join ( " lib-origin" ) ) .unwrap ( ) ;
3314
let lib_url = root.join ( " lib-origin" ) .to_string_lossy ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
3315
let lib = Repo:: clone( & lib_url, & root.join ( " lib" ) )
3316
.unwrap ( )
3317
.with_home ( root.join ( " home" ) ) ;
4dee277c Andy McCall 1 month ago ↶
3318
write ( & root.join ( " lib" ) , " lib.txt" , b " v1\n " ) ;
3319
let l1 = lib.commit ( " lib v1" , & [ ] ) .unwrap ( ) ;
3320
assert! ( lib.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
3321
let _ = lib_origin;
3322
3323
3324
let app_origin = Repo:: init( & root.join ( " app-origin" ) ) .unwrap ( ) ;
3325
let app_url = root.join ( " app-origin" ) .to_string_lossy ( ) .to_string ( ) ;
3326
let _ = app_origin;
3327
let app_dir = root.join ( " app" ) ;
75167672 Andy McCall 1 month ago
3328
let app = Repo:: clone( & app_url, & app_dir)
3329
.unwrap ( )
3330
.with_home ( root.join ( " home" ) ) ;
4dee277c Andy McCall 1 month ago ↶
3331
write ( & app_dir, " main.txt" , b " app\n " ) ;
3332
let pin = app.submodule_add ( & lib_url, " libs/lib" , None ) .unwrap ( ) ;
3333
assert! ( matches! ( & pin, crate :: subrepo:: Pin:: Oxy { heads, .. } if heads = = & vec! [ l1] ) ) ;
3334
assert! ( app_dir.join ( " .oxymodules" ) .exists ( ) & & app_dir.join ( " libs/lib/.oxy" ) .is_dir ( ) ) ;
3335
let snap = app.snapshot ( " status" ) .unwrap ( ) ;
3336
let paths: Vec< & str > = snap.statuses.iter ( ) .map ( | s | s.path.as_str ( ) ) .collect ( ) ;
75167672 Andy McCall 1 month ago
3337
assert_eq! (
3338
paths,
3339
vec! [ " .oxymodules" , " libs/lib" , " main.txt" ] ,
3340
" the nested files are not walked"
3341
) ;
4dee277c Andy McCall 1 month ago ↶
3342
let a1 = app.commit ( " app with lib" , & [ ] ) .unwrap ( ) ;
3343
let files = app.load_state ( & [ a1] ) .unwrap ( ) .materialize ( ) .files;
3344
assert_eq! ( files[ " libs/lib" ] .kind, NodeKind:: Subrepo) ;
3345
assert! ( ! files.contains_key ( " libs/lib/lib.txt" ) ) ;
3346
assert! ( ! app.snapshot ( " status" ) .unwrap ( ) .changed) ;
3347
let subs = app.subrepos ( ) .unwrap ( ) ;
3348
assert_eq! ( subs.len ( ) , 1 ) ;
3349
assert_eq! ( subs[ 0 ] .actual.as_ref ( ) , Some ( & subs[ 0 ] .pin) ) ;
3350
3351
75167672 Andy McCall 1 month ago
3352
let nested = Repo:: open( & app_dir.join ( " libs/lib" ) )
3353
.unwrap ( )
3354
.with_home ( root.join ( " home" ) ) ;
4dee277c Andy McCall 1 month ago ↶
3355
write ( & app_dir.join ( " libs/lib" ) , " lib.txt" , b " v2\n " ) ;
3356
let l2 = nested.commit ( " lib v2" , & [ ] ) .unwrap ( ) ;
3357
assert! ( nested.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
3358
let snap = app.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3359
assert_eq! (
3360
snap.statuses
3361
.iter ( )
3362
.map ( | s | ( s.path.as_str ( ) , s.status) )
3363
.collect:: < Vec< _ > > ( ) ,
3364
vec! [ ( " libs/lib" , ChangeStatus:: Modified) ]
3365
) ;
4dee277c Andy McCall 1 month ago ↶
3366
let a2 = app.commit ( " bump lib" , & [ ] ) .unwrap ( ) ;
3367
3368
let r = app.checkout ( & [ a1] , false , " checkout" ) .unwrap ( ) ;
3369
assert! ( r.notes.is_empty ( ) , " {:?}" , r.notes) ;
3370
assert_eq! ( fs:: read( app_dir.join ( " libs/lib/lib.txt" ) ) .unwrap ( ) , b " v1\n " ) ;
75167672 Andy McCall 1 month ago
3371
assert_eq! (
3372
Repo:: open( & app_dir.join ( " libs/lib" ) )
3373
.unwrap ( )
3374
.bookmark_heads ( )
3375
.unwrap ( ) ,
3376
vec! [ l1]
3377
) ;
4dee277c Andy McCall 1 month ago ↶
3378
assert! ( ! app.snapshot ( " status" ) .unwrap ( ) .changed) ;
3379
3380
write ( & app_dir.join ( " libs/lib" ) , " scratch.txt" , b " wip\n " ) ;
3381
let r = app.checkout ( & [ a2] , false , " checkout" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3382
assert! (
3383
r.notes.iter ( ) .any ( | n | n.contains ( " uncommitted" ) ) ,
3384
" {:?}" ,
3385
r.notes
3386
) ;
4dee277c Andy McCall 1 month ago ↶
3387
assert_eq! ( fs:: read( app_dir.join ( " libs/lib/lib.txt" ) ) .unwrap ( ) , b " v1\n " ) ;
3388
fs:: remove_file( app_dir.join ( " libs/lib/scratch.txt" ) ) .unwrap ( ) ;
3389
let r = app.submodule_update ( false , & [ ] ) .unwrap ( ) ;
3390
assert_eq! ( r, vec! [ ( " libs/lib" .to_string ( ) , SubrepoSync:: Updated) ] ) ;
3391
assert_eq! ( fs:: read( app_dir.join ( " libs/lib/lib.txt" ) ) .unwrap ( ) , b " v2\n " ) ;
3392
let _ = l2;
3393
3394
3395
assert! ( app.push ( " origin" , false ) .unwrap ( ) [ 0 ] .ok) ;
3396
let clone_dir = root.join ( " app-clone" ) ;
75167672 Andy McCall 1 month ago
3397
let clone = Repo:: clone( & app_url, & clone_dir)
3398
.unwrap ( )
3399
.with_home ( root.join ( " home" ) ) ;
4dee277c Andy McCall 1 month ago ↶
3400
assert! ( ! clone_dir.join ( " libs/lib/.oxy" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
3401
assert_eq! (
3402
clone.submodule_update ( false , & [ ] ) .unwrap ( ) ,
3403
vec! [ ( " libs/lib" .to_string ( ) , SubrepoSync:: Missing) ]
3404
) ;
3405
assert_eq! (
3406
clone.submodule_update ( true , & [ ] ) .unwrap ( ) ,
3407
vec! [ ( " libs/lib" .to_string ( ) , SubrepoSync:: Updated) ]
3408
) ;
3409
assert_eq! (
3410
fs:: read( clone_dir.join ( " libs/lib/lib.txt" ) ) .unwrap ( ) ,
3411
b " v2\n "
3412
) ;
4dee277c Andy McCall 1 month ago ↶
3413
assert! ( ! clone.snapshot ( " status" ) .unwrap ( ) .changed) ;
3414
3415
clone.submodule_remove ( " libs/lib" ) .unwrap ( ) ;
3416
let snap = clone.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3417
assert! (
3418
snap.statuses
3419
.iter ( )
3420
.any ( | s | s.path = = " libs/lib" & & s.status = = ChangeStatus:: Removed ) ,
3421
" {:?}" ,
3422
snap.statuses
3423
) ;
4dee277c Andy McCall 1 month ago ↶
3424
let c = clone.commit ( " drop lib" , & [ ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3425
assert! ( ! clone
3426
.load_state ( & [ c] )
3427
.unwrap ( )
3428
.materialize ( )
3429
.files
3430
.contains_key ( " libs/lib" ) ) ;
4dee277c Andy McCall 1 month ago ↶
3431
assert! ( clone_dir.join ( " libs/lib/lib.txt" ) .exists ( ) ) ;
75167672 Andy McCall 1 month ago
3432
assert! (
3433
! clone.snapshot ( " status" ) .unwrap ( ) .changed,
3434
" an unregistered nested repository is ignored"
3435
) ;
4dee277c Andy McCall 1 month ago ↶
3436
}
3437
3438
# [ test ]
3439
fn git_submodules_import_and_export_as_gitlinks ( ) {
3440
hermetic ( ) ;
75167672 Andy McCall 1 month ago
3441
if std:: process:: Command:: new( " git" )
3442
.arg ( " --version" )
3443
.output ( )
3444
.is_err ( )
3445
{
4dee277c Andy McCall 1 month ago ↶
3446
eprintln! ( " git not installed; skipping" ) ;
3447
return ;
3448
}
3449
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3450
let root = dir.path ( ) ;
3451
let git_in = | d : & Path, args : & [& str ]| -> String {
3452
let out = std:: process:: Command:: new( " git" )
3453
.arg ( " -C" )
3454
.arg ( d)
75167672 Andy McCall 1 month ago
3455
.args ( [
3456
" -c" ,
3457
" user.name=Grace" ,
3458
" -c" ,
3459
" [email protected] " ,
3460
" -c" ,
3461
" commit.gpgsign=false" ,
3462
" -c" ,
3463
" protocol.file.allow=always" ,
3464
] )
4dee277c Andy McCall 1 month ago ↶
3465
.args ( args)
3466
.output ( )
3467
.unwrap ( ) ;
75167672 Andy McCall 1 month ago
3468
assert! (
3469
out.status.success ( ) ,
3470
" git {args:?}: {}" ,
3471
String :: from_utf8_lossy( & out.stderr)
3472
) ;
4dee277c Andy McCall 1 month ago ↶
3473
String :: from_utf8_lossy( & out.stdout) .trim ( ) .to_string ( )
3474
} ;
3475
let sub = root.join ( " sub" ) ;
3476
fs:: create_dir_all( & sub) .unwrap ( ) ;
3477
git_in ( & sub, & [ " init" , " -q" , " -b" , " main" ] ) ;
3478
write ( & sub, " s.txt" , b " sub\n " ) ;
3479
git_in ( & sub, & [ " add" , " ." ] ) ;
3480
git_in ( & sub, & [ " commit" , " -q" , " -m" , " sub initial" ] ) ;
3481
let sub_sha = git_in ( & sub, & [ " rev-parse" , " HEAD" ] ) ;
3482
let g = root.join ( " main.git" ) ;
3483
fs:: create_dir_all( & g) .unwrap ( ) ;
3484
git_in ( & g, & [ " init" , " -q" , " -b" , " main" ] ) ;
3485
write ( & g, " README.md" , b " # app\n " ) ;
3486
git_in ( & g, & [ " add" , " ." ] ) ;
3487
git_in ( & g, & [ " commit" , " -q" , " -m" , " initial" ] ) ;
75167672 Andy McCall 1 month ago
3488
git_in (
3489
& g,
3490
& [
3491
" submodule" ,
3492
" add" ,
3493
" -q" ,
3494
sub.to_str ( ) .unwrap ( ) ,
3495
" vendor/sub" ,
3496
] ,
3497
) ;
4dee277c Andy McCall 1 month ago ↶
3498
git_in ( & g, & [ " commit" , " -q" , " -m" , " add submodule" ] ) ;
3499
3500
let dest = root.join ( " oxy" ) ;
3501
let repo = Repo:: init( & dest) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3502
let rep = repo.import_git ( & g, " main" ) .unwrap ( ) ;
3503
assert_eq! ( ( rep.commits, rep.submodules) , ( 2 , 1 ) ) ;
75167672 Andy McCall 1 month ago
3504
let files = repo
3505
.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) )
3506
.unwrap ( )
3507
.materialize ( )
3508
.files;
4dee277c Andy McCall 1 month ago ↶
3509
assert_eq! ( files[ " vendor/sub" ] .kind, NodeKind:: Subrepo) ;
75167672 Andy McCall 1 month ago
3510
let FileContent:: Opaque { blobs } = & files[ " vendor/sub" ] .content else {
3511
panic! ( )
3512
} ;
3513
assert_eq! (
3514
crate :: subrepo:: Pin:: decode( & repo.read_blob ( & blobs[ 0 ] ) .unwrap ( ) ) .unwrap ( ) ,
3515
crate :: subrepo:: Pin:: Git {
3516
sha: sub_sha.clone ( ) ,
3517
branch: " HEAD" .into ( )
3518
}
3519
) ;
3520
let modules =
3521
crate :: subrepo:: Modules:: parse( & fs:: read_to_string( dest.join ( " .oxymodules" ) ) .unwrap ( ) ) ;
4dee277c Andy McCall 1 month ago ↶
3522
assert_eq! ( modules.entries[ " vendor/sub" ] .url, sub.to_string_lossy ( ) ) ;
3523
assert! ( modules.entries[ " vendor/sub" ] .git) ;
75167672 Andy McCall 1 month ago
3524
assert! (
3525
files.contains_key ( " .gitmodules" ) ,
3526
" the Git file is kept for round trips"
3527
) ;
3528
assert! (
3529
! dest.join ( " vendor/sub/.git" ) .exists ( ) ,
3530
" not cloned until update --init"
3531
) ;
4dee277c Andy McCall 1 month ago ↶
3532
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3533
75167672 Andy McCall 1 month ago
3534
assert_eq! (
3535
repo.submodule_update ( true , & [ ] ) .unwrap ( ) ,
3536
vec! [ ( " vendor/sub" .to_string ( ) , SubrepoSync:: Updated) ]
3537
) ;
3538
assert_eq! (
3539
git_in ( & dest.join ( " vendor/sub" ) , & [ " rev-parse" , " HEAD" ] ) ,
3540
sub_sha
3541
) ;
4dee277c Andy McCall 1 month ago ↶
3542
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3543
3544
let mirror = root.join ( " mirror.git" ) ;
3545
let rep = repo.git_export ( & mirror) .unwrap ( ) ;
3546
assert_eq! ( rep.skipped_subrepos, 0 ) ;
3547
let ls = git_in ( & mirror, & [ " ls-tree" , " main" , " vendor/sub" ] ) ;
75167672 Andy McCall 1 month ago
3548
assert! (
3549
ls.starts_with ( " 160000 commit " ) & & ls.contains ( & sub_sha) ,
3550
" {ls}"
3551
) ;
3552
assert_eq! (
3553
git_in ( & mirror, & [ " ls-tree" , " --name-only" , " main" ] ) ,
3554
" .gitmodules\n README.md\n vendor"
3555
) ;
4dee277c Andy McCall 1 month ago ↶
3556
let orig_tree = git_in ( & g, & [ " rev-parse" , " main^{tree}" ] ) ;
75167672 Andy McCall 1 month ago
3557
assert_eq! (
3558
git_in ( & mirror, & [ " rev-parse" , " main^{tree}" ] ) ,
3559
orig_tree,
3560
" tree round trip"
3561
) ;
4dee277c Andy McCall 1 month ago ↶
3562
}
3563
957fee30 Andy McCall 1 month ago ↶
3564
# [ test ]
3565
fn partial_commits_by_path_and_by_hunk ( ) {
3566
hermetic ( ) ;
3567
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3568
let root = dir.path ( ) ;
3569
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3570
write ( root, " a.txt" , b " 1\n 2\n 3\n " ) ;
3571
write ( root, " b.txt" , b " b\n " ) ;
3572
let h0 = repo.commit ( " base" , & [ ] ) .unwrap ( ) ;
3573
write ( root, " a.txt" , b " 0\n 1\n 2\n 3\n 4\n " ) ;
3574
write ( root, " b.txt" , b " b\n bb\n " ) ;
3575
write ( root, " c.txt" , b " c\n " ) ;
3576
75167672 Andy McCall 1 month ago
3577
let h1 = repo
3578
.commit_selection ( " a only" , & [ ] , & Selection:: Paths( vec! [ " a.txt" .into ( ) ] ) )
3579
.unwrap ( ) ;
957fee30 Andy McCall 1 month ago ↶
3580
let c1 = repo.store ( ) .get_change ( & h1) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3581
assert! (
3582
c1.tree_ops.is_empty ( ) & & c1.edits.iter ( ) .all ( | e | e.node ( ) = = c1.edits[ 0 ] .node ( ) ) ,
3583
" {:?}" ,
3584
c1.tree_ops
3585
) ;
957fee30 Andy McCall 1 month ago ↶
3586
assert_eq! ( c1.deps, vec! [ h0] ) ;
3587
let files = repo.load_state ( & [ h1] ) .unwrap ( ) .materialize ( ) .files;
75167672 Andy McCall 1 month ago
3588
assert! (
3589
matches! ( & files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " 0\n 1\n 2\n 3\n 4\n " )
3590
) ;
957fee30 Andy McCall 1 month ago ↶
3591
assert! ( matches! ( & files[ " b.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " b\n " ) ) ;
3592
assert! ( ! files.contains_key ( " c.txt" ) ) ;
3593
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3594
assert_eq! (
3595
snap.statuses
3596
.iter ( )
3597
.map ( | s | ( s.path.as_str ( ) , s.status) )
3598
.collect:: < Vec< _ > > ( ) ,
3599
vec! [
3600
( " b.txt" , ChangeStatus:: Modified) ,
3601
( " c.txt" , ChangeStatus:: Added)
3602
]
3603
) ;
3604
assert_eq! (
3605
fs:: read( root.join ( " b.txt" ) ) .unwrap ( ) ,
3606
b " b\n bb\n " ,
3607
" disk untouched"
3608
) ;
957fee30 Andy McCall 1 month ago ↶
3609
3610
let ( _ , hunks) = repo.hunks ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3611
let b_hunk = hunks
3612
.iter ( )
3613
.find ( | h | h.path = = " b.txt" & & h.text.contains ( " +bb" ) )
3614
.unwrap ( )
3615
.index;
3616
let h2 = repo
3617
.commit_selection ( " b only" , & [ ] , & Selection:: Hunks( vec! [ b_hunk] ) )
3618
.unwrap ( ) ;
957fee30 Andy McCall 1 month ago ↶
3619
let files = repo.load_state ( & [ h2] ) .unwrap ( ) .materialize ( ) .files;
75167672 Andy McCall 1 month ago
3620
assert! (
3621
matches! ( & files[ " b.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " b\n bb\n " )
3622
) ;
957fee30 Andy McCall 1 month ago ↶
3623
assert! ( ! files.contains_key ( " c.txt" ) ) ;
3624
let snap = repo.snapshot ( " status" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3625
assert_eq! (
3626
snap.statuses
3627
.iter ( )
3628
.map ( | s | s.path.as_str ( ) )
3629
.collect:: < Vec< _ > > ( ) ,
3630
vec! [ " c.txt" ]
3631
) ;
957fee30 Andy McCall 1 month ago ↶
3632
3633
3634
write ( root, " d/e.txt" , b " e\n " ) ;
3635
let ( _ , hunks) = repo.hunks ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3636
let e_insert = hunks
3637
.iter ( )
3638
.find ( | h | h.path = = " d/e.txt" & & h.text.contains ( " +e" ) )
3639
.unwrap ( )
3640
.index;
3641
let h3 = repo
3642
.commit_selection ( " e only" , & [ ] , & Selection:: Hunks( vec! [ e_insert] ) )
3643
.unwrap ( ) ;
957fee30 Andy McCall 1 month ago ↶
3644
let c3 = repo.store ( ) .get_change ( & h3) .unwrap ( ) ;
3645
assert_eq! ( c3.tree_ops.len ( ) , 2 , " {:?}" , c3.tree_ops) ;
3646
let files = repo.load_state ( & [ h3] ) .unwrap ( ) .materialize ( ) .files;
3647
assert! ( files.contains_key ( " d/e.txt" ) & & ! files.contains_key ( " c.txt" ) ) ;
d8c086ca Andy McCall 1 month ago ↶
3648
3649
write ( root, " f/g.txt" , b " g\n " ) ;
3650
let ( _ , hunks) = repo.hunks ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3651
let g_add = hunks
3652
.iter ( )
3653
.find ( | h | h.path = = " f/g.txt" & & h.text.starts_with ( " A " ) )
3654
.unwrap ( )
3655
.index;
3656
let h4 = repo
3657
.commit_selection ( " g by addition" , & [ ] , & Selection:: Hunks( vec! [ g_add] ) )
3658
.unwrap ( ) ;
d8c086ca Andy McCall 1 month ago ↶
3659
let files = repo.load_state ( & [ h4] ) .unwrap ( ) .materialize ( ) .files;
75167672 Andy McCall 1 month ago
3660
assert! (
3661
matches! ( & files[ " f/g.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " g\n " )
3662
) ;
3663
assert! ( ! repo
3664
.snapshot ( " status" )
3665
.unwrap ( )
3666
.statuses
3667
.iter ( )
3668
.any ( | s | s.path = = " f/g.txt" ) ) ;
957fee30 Andy McCall 1 month ago ↶
3669
3670
repo.commit ( " the rest" , & [ ] ) .unwrap ( ) ;
3671
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
75167672 Andy McCall 1 month ago
3672
let files = repo
3673
.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) )
3674
.unwrap ( )
3675
.materialize ( )
3676
.files;
957fee30 Andy McCall 1 month ago ↶
3677
assert! ( files.contains_key ( " c.txt" ) ) ;
3678
}
3679
3680
# [ test ]
3681
fn blame_attributes_lines_to_introducing_changes_across_renames ( ) {
3682
hermetic ( ) ;
3683
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3684
let root = dir.path ( ) ;
3685
let mut repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
75167672 Andy McCall 1 month ago
3686
repo.create_identity ( " Ada" , " [email protected] " , IdentityScope:: User)
3687
.unwrap ( ) ;
957fee30 Andy McCall 1 month ago ↶
3688
write ( root, " a.txt" , b " one\n " ) ;
3689
let h1 = repo.commit ( " first" , & [ ] ) .unwrap ( ) ;
3690
write ( root, " a.txt" , b " one\n two\n " ) ;
3691
let h2 = repo.commit ( " second" , & [ ] ) .unwrap ( ) ;
3692
let b = repo.blame ( " a.txt" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3693
assert_eq! (
3694
b.iter ( )
3695
.map ( | l | ( l.line, l.hash, l.text.as_slice ( ) ) )
3696
.collect:: < Vec< _ > > ( ) ,
3697
vec! [ ( 1 , h1, b " one\n " .as_slice ( ) ) , ( 2 , h2, b " two\n " .as_slice ( ) ) ]
3698
) ;
957fee30 Andy McCall 1 month ago ↶
3699
assert! ( b.iter ( ) .all ( | l | l.author = = " Ada" ) ) ;
75167672 Andy McCall 1 month ago
3700
assert_eq! (
3701
b[ 0 ] .change_id,
3702
repo.store ( ) .get_change ( & h1) .unwrap ( ) .change_id
3703
) ;
957fee30 Andy McCall 1 month ago ↶
3704
fs:: rename( root.join ( " a.txt" ) , root.join ( " b.txt" ) ) .unwrap ( ) ;
3705
repo.commit ( " rename" , & [ ] ) .unwrap ( ) ;
3706
let b = repo.blame ( " b.txt" ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3707
assert_eq! (
3708
b.iter ( ) .map ( | l | l.hash ) .collect:: < Vec< _ > > ( ) ,
3709
vec! [ h1, h2] ,
3710
" the node survives the rename"
3711
) ;
957fee30 Andy McCall 1 month ago ↶
3712
assert! ( repo.blame ( " a.txt" ) .is_err ( ) ) ;
3713
}
3714
3715
# [ test ]
3716
fn bisect_finds_the_first_bad_change ( ) {
3717
hermetic ( ) ;
3718
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3719
let root = dir.path ( ) ;
3720
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3721
let mut content = String :: new( ) ;
3722
let mut hashes = Vec :: new( ) ;
3723
for i in 1 .. = 8 {
3724
content.push_str ( & format! ( " line {i} {} \n " , if i = = 5 { " bug" } else { " " } ) ) ;
3725
write ( root, " f.txt" , content.as_bytes ( ) ) ;
3726
hashes.push ( repo.commit ( & format! ( " step {i} " ) , & [ ] ) .unwrap ( ) ) ;
3727
}
3728
3729
assert_eq! ( repo.bookmark_heads ( ) .unwrap ( ) , vec! [ hashes[ 7 ] ] ) ;
3730
repo.bisect_start ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3731
assert! ( matches! (
3732
repo.bisect_mark ( Some ( true ) , & [ hashes[ 0 ] ] ) .unwrap ( ) ,
3733
BisectStep:: NeedMarks { good: 1 , bad: 0 }
3734
) ) ;
957fee30 Andy McCall 1 month ago ↶
3735
let step = repo.bisect_mark ( Some ( false ) , & [ hashes[ 7 ] ] ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3736
assert! (
3737
matches! ( step, BisectStep:: Testing { remaining: 7 , .. } ) ,
3738
" {step:?}"
3739
) ;
957fee30 Andy McCall 1 month ago ↶
3740
75167672 Andy McCall 1 month ago
3741
let step = repo
3742
.bisect_run ( & [ " sh" .into ( ) , " -c" .into ( ) , " ! grep -q bug f.txt" .into ( ) ] )
3743
.unwrap ( ) ;
957fee30 Andy McCall 1 month ago ↶
3744
assert_eq! ( step, BisectStep:: Found { culprit: hashes[ 4 ] } ) ;
75167672 Andy McCall 1 month ago
3745
assert_eq! (
3746
repo.bisect_status ( ) .unwrap ( ) ,
3747
Some ( BisectStep:: Found { culprit: hashes[ 4 ] } )
3748
) ;
957fee30 Andy McCall 1 month ago ↶
3749
repo.bisect_reset ( ) .unwrap ( ) ;
3750
assert_eq! ( fs:: read_to_string( root.join ( " f.txt" ) ) .unwrap ( ) , content) ;
3751
assert! ( repo.bisect_status ( ) .unwrap ( ) .is_none ( ) ) ;
3752
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3753
3754
repo.bisect_start ( ) .unwrap ( ) ;
3755
repo.bisect_mark ( Some ( true ) , & [ hashes[ 0 ] ] ) .unwrap ( ) ;
3756
let mut step = repo.bisect_mark ( Some ( false ) , & [ hashes[ 7 ] ] ) .unwrap ( ) ;
3757
let mut skipped_once = false ;
3758
let mut guard = 0 ;
3759
while let BisectStep:: Testing { .. } = step {
3760
guard + = 1 ;
3761
assert! ( guard < 20 ) ;
3762
let text = fs:: read_to_string( root.join ( " f.txt" ) ) .unwrap ( ) ;
3763
if ! skipped_once & & text.contains ( " line 6" ) {
3764
3765
skipped_once = true ;
3766
step = repo.bisect_mark ( None , & [ ] ) .unwrap ( ) ;
3767
continue ;
3768
}
3769
step = repo.bisect_mark ( Some ( ! text.contains ( " bug" ) ) , & [ ] ) .unwrap ( ) ;
3770
}
3771
assert! ( skipped_once) ;
3772
assert_eq! ( step, BisectStep:: Found { culprit: hashes[ 4 ] } ) ;
3773
repo.bisect_reset ( ) .unwrap ( ) ;
3774
3775
repo.bisect_start ( ) .unwrap ( ) ;
3776
repo.bisect_mark ( Some ( true ) , & [ hashes[ 0 ] ] ) .unwrap ( ) ;
3777
let mut step = repo.bisect_mark ( Some ( false ) , & [ hashes[ 7 ] ] ) .unwrap ( ) ;
3778
let mut guard = 0 ;
3779
while let BisectStep:: Testing { heads, .. } = & step {
3780
guard + = 1 ;
3781
assert! ( guard < 20 ) ;
3782
let text = fs:: read_to_string( root.join ( " f.txt" ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3783
let is_culprit_state =
3784
heads.contains ( & hashes[ 4 ] ) & & ! heads.iter ( ) .any ( | h | hashes[ 5 .. ] .contains ( h) ) ;
3785
step = if is_culprit_state {
3786
repo.bisect_mark ( None , & [ ] ) .unwrap ( )
3787
} else {
3788
repo.bisect_mark ( Some ( ! text.contains ( " bug" ) ) , & [ ] ) .unwrap ( )
3789
} ;
957fee30 Andy McCall 1 month ago ↶
3790
}
3791
match step {
75167672 Andy McCall 1 month ago
3792
BisectStep:: Inconclusive { candidates } => assert! (
3793
candidates.contains ( & hashes[ 4 ] ) & & candidates.len ( ) < = 2 ,
3794
" {candidates:?}"
3795
) ,
957fee30 Andy McCall 1 month ago ↶
3796
other => panic! ( " {other:?}" ) ,
3797
}
3798
repo.bisect_reset ( ) .unwrap ( ) ;
3799
}
3800
728da0f9 Andy McCall 1 month ago ↶
3801
# [ test ]
3802
fn squash_folds_a_change_into_its_dependency ( ) {
3803
hermetic ( ) ;
3804
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3805
let root = dir.path ( ) ;
3806
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3807
write ( root, " a.txt" , b " 1\n " ) ;
3808
let a = repo.commit ( " A" , & [ ] ) .unwrap ( ) ;
3809
write ( root, " a.txt" , b " 1\n 2\n " ) ;
3810
let c = repo.commit ( " C" , & [ ] ) .unwrap ( ) ;
3811
write ( root, " a.txt" , b " 1\n 2\n 3\n " ) ;
3812
let e = repo.commit ( " E" , & [ ] ) .unwrap ( ) ;
3813
write ( root, " b.txt" , b " b\n " ) ;
3814
let b = repo.commit ( " B" , & [ ] ) .unwrap ( ) ;
3815
let id_a = repo.store ( ) .get_change ( & a) .unwrap ( ) .change_id;
3816
let id_e = repo.store ( ) .get_change ( & e) .unwrap ( ) .change_id;
3817
3818
let err = repo.squash ( & e, & a) .unwrap_err ( ) .to_string ( ) ;
3819
assert! ( err.contains ( & c.short ( 12 ) ) , " {err}" ) ;
3820
3821
let a2 = repo.squash ( & c, & a) .unwrap ( ) ;
3822
let a2c = repo.store ( ) .get_change ( & a2) .unwrap ( ) ;
3823
assert_eq! ( a2c.change_id, id_a) ;
3824
assert! ( a2c.deps.is_empty ( ) ) ;
3825
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
3826
let st = repo.load_state ( & heads) .unwrap ( ) ;
3827
assert! ( ! st.contains ( & c) & & ! st.contains ( & a) & & ! st.contains ( & e) ) ;
75167672 Andy McCall 1 month ago
3828
let e2 = st
3829
.changes ( )
3830
.find ( | ( _, m ) | m.change_id = = id_e )
3831
.map ( | ( h , _) | * h)
3832
.unwrap ( ) ;
3833
assert_eq! (
3834
repo.store ( ) .get_change ( & e2) .unwrap ( ) .deps,
3835
vec! [ a2] ,
3836
" E now depends on A' alone"
3837
) ;
728da0f9 Andy McCall 1 month ago ↶
3838
let files = st.materialize ( ) .files;
75167672 Andy McCall 1 month ago
3839
assert! (
3840
matches! ( & files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " 1\n 2\n 3\n " )
3841
) ;
728da0f9 Andy McCall 1 month ago ↶
3842
assert! ( files.contains_key ( " b.txt" ) ) ;
3843
assert! ( st.contains ( & b) ) ;
3844
assert! ( ! repo.snapshot ( " status" ) .unwrap ( ) .changed) ;
3845
assert_eq! ( repo.log ( ) .unwrap ( ) .len ( ) , 4 , " working copy, B, E', A'" ) ;
3846
3847
repo.undo ( ) .unwrap ( ) ;
3848
repo.undo ( ) .unwrap ( ) ;
3849
let st = repo.load_state ( & repo.bookmark_heads ( ) .unwrap ( ) ) .unwrap ( ) ;
3850
assert! ( st.contains ( & c) & & st.contains ( & a) & & st.contains ( & e) ) ;
3851
}
3852
3853
# [ test ]
3854
fn rebase_re_expresses_a_change_against_another_base ( ) {
3855
hermetic ( ) ;
3856
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3857
let root = dir.path ( ) ;
3858
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3859
write ( root, " a.txt" , b " 1\n 2\n " ) ;
3860
let a = repo.commit ( " A" , & [ ] ) .unwrap ( ) ;
3861
write ( root, " a.txt" , b " 1\n 2\n 3\n " ) ;
3862
let c = repo.commit ( " C: append 3" , & [ ] ) .unwrap ( ) ;
3863
write ( root, " a.txt" , b " 1\n 2\n 3\n 4\n " ) ;
3864
let e = repo.commit ( " E: append 4" , & [ ] ) .unwrap ( ) ;
3865
3866
repo.checkout ( & [ a] , true , " checkout" ) .unwrap ( ) ;
3867
write ( root, " a.txt" , b " 0\n 1\n 2\n " ) ;
3868
let d = repo.commit ( " D: prepend 0" , & [ ] ) .unwrap ( ) ;
3869
let id_c = repo.store ( ) .get_change ( & c) .unwrap ( ) .change_id;
3870
let id_e = repo.store ( ) .get_change ( & e) .unwrap ( ) .change_id;
3871
3872
let c2 = repo.rebase ( & c, & [ d] ) .unwrap ( ) ;
3873
let c2c = repo.store ( ) .get_change ( & c2) .unwrap ( ) ;
3874
assert_eq! ( c2c.change_id, id_c) ;
3875
assert! ( c2c.deps.contains ( & d) , " {:?}" , c2c.deps) ;
3876
let heads = repo.bookmark_heads ( ) .unwrap ( ) ;
3877
let st = repo.load_state ( & heads) .unwrap ( ) ;
3878
assert! ( ! st.contains ( & c) & & st.contains ( & d) ) ;
75167672 Andy McCall 1 month ago
3879
let e2 = st
3880
.changes ( )
3881
.find ( | ( _, m ) | m.change_id = = id_e )
3882
.map ( | ( h , _) | * h)
3883
.unwrap ( ) ;
728da0f9 Andy McCall 1 month ago ↶
3884
let e2deps = repo.store ( ) .get_change ( & e2) .unwrap ( ) .deps;
75167672 Andy McCall 1 month ago
3885
assert! (
3886
e2deps.contains ( & c2) & & ! e2deps.contains ( & c) & & e2deps.contains ( & a) ,
3887
" E follows C by content and still depends on A, which introduced the file: {e2deps:?}"
3888
) ;
728da0f9 Andy McCall 1 month ago ↶
3889
let files = st.materialize ( ) .files;
75167672 Andy McCall 1 month ago
3890
assert! (
3891
matches! ( & files[ " a.txt" ] .content, FileContent:: Text { bytes, .. } if bytes = = b " 0\n 1\n 2\n 3\n 4\n " ) ,
3892
" {:?}" ,
3893
files[ " a.txt" ] .content
3894
) ;
728da0f9 Andy McCall 1 month ago ↶
3895
assert! ( ! files[ " a.txt" ] .is_conflicted ( ) ) ;
3896
3897
assert! ( repo.rebase ( & c2, & [ c2] ) .is_err ( ) ) ;
3898
let err = repo.rebase ( & e2, & [ d] ) .unwrap_err ( ) .to_string ( ) ;
75167672 Andy McCall 1 month ago
3899
assert! (
3900
err.contains ( " lacks dependencies" ) ,
3901
" E needs C, which D's state does not have: {err}"
3902
) ;
728da0f9 Andy McCall 1 month ago ↶
3903
}
3904
c792033a Andy McCall 1 month ago ↶
3905
# [ test ]
3906
fn unsigned_tags_keep_their_message ( ) {
3907
hermetic ( ) ;
3908
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3909
let root = dir.path ( ) ;
3910
let repo = Repo:: init( root) .unwrap ( ) .with_home ( root.join ( " home" ) ) ;
3911
write ( root, " a.txt" , b " a\n " ) ;
3912
repo.commit ( " a" , & [ ] ) .unwrap ( ) ;
3913
assert! ( repo.secret_key ( ) .unwrap ( ) .is_none ( ) ) ;
75167672 Andy McCall 1 month ago
3914
repo.tag_create ( " v1" , & repo.bookmark_heads ( ) .unwrap ( ) , " first release" )
3915
.unwrap ( ) ;
c792033a Andy McCall 1 month ago ↶
3916
let tags = repo.tags ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3917
assert_eq! (
3918
(
3919
tags[ 0 ] .name.as_str ( ) ,
3920
tags[ 0 ] .message.as_str ( ) ,
3921
tags[ 0 ] .signer.as_deref ( )
3922
) ,
3923
( " v1" , " first release" , None )
3924
) ;
3925
assert! (
3926
repo.tag_refs ( ) .unwrap ( ) [ 0 ] .attestation.is_none ( ) ,
3927
" an unsigned attestation is not advertised"
3928
) ;
c792033a Andy McCall 1 month ago ↶
3929
}