Oregami
Repositories/oxedyne/ore

oxedyne/ore/cli/src/verbs.rs

55.0 KiB, 180 runs

created by r2848102244:21, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! The verbs, one function each.
2//!
3//! Every verb but `init` opens the repository, captures the working copy, and
4//! only then does what it was asked. Output is plain text with no colour codes,
5//! because the first consumer of this tool after a person is an agent reading
6//! its standard output.
7
8use crate::capture;
9use crate::collisions;
10use crate::listing::{
11 Listing,
12 Point,
13};
14use crate::keys::{
15 self,
16 mark_of,
17};
18use crate::place::Where;
19use crate::repo::{
20 self,
21 Repo,
22};
23use crate::revert;
24use crate::reviewed::{
25 self,
26 Reviewed,
27};
28use crate::tree;
29
30use ore_store::repack::{
31 self,
32 Packing,
33};
34use ore_store::store::Verify;
35use ore_store::sweep;
36use ore_store::veilkey::{
37 self,
38 Wrap,
39};
40use ore_store::verdict::{
41 self,
42 Verdicts,
43 VERDICT_LIFE,
44};
45
46use oxedyne_fe2o3_core::prelude::*;
47use oxedyne_fe2o3_ore::id::{
48 OpId,
49 ReplicaId,
50};
51use oxedyne_fe2o3_ore::op::Op;
52use oxedyne_fe2o3_ore::seq::OpOrder;
53use oxedyne_fe2o3_ore::seq::render::{
54 Flag,
55 Repo as Render,
56};
57
58use std::collections::BTreeSet;
59use std::path::Path;
60
61
62/// Longest stretch of a run's content shown by `who`.
63const SHOW_LIMIT: usize = 40;
64
65
66/// Renders bytes so that a terminal survives them, cutting at `limit`.
67pub fn show(bytes: &[u8], limit: usize) -> String {
68 let mut out = String::new();
69 for byte in bytes.iter().take(limit) {
70 match byte {
71 b'\n' => out.push_str("\\n"),
72 b'\r' => out.push_str("\\r"),
73 b'\t' => out.push_str("\\t"),
74 b'\\' => out.push_str("\\\\"),
75 0x20..=0x7e => out.push(*byte as char),
76 other => out.push_str(&fmt!("\\x{:02x}", other)),
77 }
78 }
79 if bytes.len() > limit {
80 out.push('~');
81 }
82 out
83}
84
85/// Writes a one line summary of what a capture recorded.
86pub fn report(what: &capture::Captured) {
87 if what.is_empty() {
88 println!("captured nothing; the working copy is what the history says");
89 return;
90 }
91 let mut parts: Vec<String> = Vec::new();
92 if !what.created.is_empty() {
93 parts.push(fmt!("{} created", what.created.len()));
94 }
95 if !what.edited.is_empty() {
96 parts.push(fmt!("{} edited", what.edited.len()));
97 }
98 if !what.deleted.is_empty() {
99 parts.push(fmt!("{} deleted", what.deleted.len()));
100 }
101 if !what.moved.is_empty() {
102 parts.push(fmt!("{} moved as a block", what.moved.len()));
103 }
104 println!("captured {} operation{}: {}",
105 what.ops,
106 if what.ops == 1 { "" } else { "s" },
107 parts.join(", "),
108 );
109 for path in &what.created {
110 println!(" created {}", tree::shown(path));
111 }
112 // An edit says what it cost, because that is the whole point of finding a
113 // real difference: a small change to a large file stores the small change.
114 for edit in &what.edited {
115 println!(" edited {} {} operation{}, {} byte{} inserted",
116 tree::shown(&edit.path),
117 edit.ops, if edit.ops == 1 { "" } else { "s" },
118 edit.inserted, if edit.inserted == 1 { "" } else { "s" },
119 );
120 }
121 for path in &what.deleted {
122 println!(" deleted {}", tree::shown(path));
123 }
124 // A move is worth a line of its own: it is the one thing a capture records
125 // that keeps its bytes' authors, and the edits above account for it as a
126 // single operation rather than a deletion and an insertion.
127 for mv in &what.moved {
128 println!(" moved {} byte{} from {} to {}",
129 mv.bytes, if mv.bytes == 1 { "" } else { "s" },
130 tree::shown(&mv.from),
131 tree::shown(&mv.to),
132 );
133 }
134}
135
136/// Writes what a clash did, which is a decision this tool made and the
137/// repository did not.
138fn report_clashes(clashes: &[tree::Clash]) {
139 for clash in clashes {
140 println!("clash at {}: {} files hold that path",
141 tree::shown(&clash.path), clash.moved.len() + 1);
142 println!(" {} keeps the name, being highest in op order", clash.kept);
143 for (file, name) in &clash.moved {
144 println!(" {} is written as {}", file, tree::shown(name));
145 }
146 }
147}
148
149/// Writes a frontier as a line of text, in the canonical ascending order the
150/// sync protocol spells one in.
151///
152/// Two replicas that agree hold the same operations, and the frontier is the
153/// short way of saying so: it is a set, so it is written sorted, and two
154/// repositories that print the same line hold the same history.
155pub fn frontier_of(heads: &[OpId]) -> String {
156 if heads.is_empty() {
157 return fmt!("empty, nothing has been written");
158 }
159 let mut sorted = heads.to_vec();
160 sorted.sort();
161 let shown: Vec<String> = sorted.iter().map(|id| fmt!("{}", id)).collect();
162 shown.join(", ")
163}
164
165/// Summarises a list of parents for a log line.
166fn parents_of(ids: &[OpId]) -> String {
167 if ids.is_empty() {
168 return fmt!("none, a root");
169 }
170 let shown: Vec<String> = ids.iter().take(3).map(|id| fmt!("{}", id)).collect();
171 if ids.len() > 3 {
172 fmt!("{} and {} more", shown.join(", "), ids.len() - 3)
173 } else {
174 shown.join(", ")
175 }
176}
177
178
179/// `ore init` -- creates a repository, mints a replica identifier and a signing
180/// key, writes the configuration and an empty log.
181///
182/// With `signed` false no key is minted and the repository writes bare records.
183/// That is the state every repository made before this tool signed anything is
184/// in, and `ore key` is how one leaves it.
185pub fn init(dir: &Path, signed: bool, mirror: Option<String>)
186 -> Outcome<()>
187{
188 match std::fs::create_dir_all(dir) {
189 Ok(()) => (),
190 Err(e) => return Err(err!(e,
191 "The directory {:?} could not be created.", dir;
192 IO, File, Write)),
193 }
194 let root = match std::fs::canonicalize(dir) {
195 Ok(p) => p,
196 Err(e) => return Err(err!(e,
197 "The directory {:?} could not be resolved.", dir;
198 IO, File, Read)),
199 };
200 let mut repo = res!(Repo::init(&root, signed));
201 if let Some(path) = mirror {
202 repo.cfg.mirror = Some(path.clone());
203 res!(repo.save_config());
204 println!("git mirror at {}", path);
205 println!("every verb that appends operations brings it current, and there is \
206 no export verb to remember");
207 }
208 println!("initialised an Ore repository in {}", root.display());
209 println!("replica {}", repo.cfg.replica);
210 match &repo.signer {
211 Some(key) => {
212 println!("signing key {} {}", keys::SCHEME, key.public_text());
213 println!("every operation this replica writes carries it; the secret key is \
214 in {} and is not encrypted",
215 repo.dir.join(keys::KEY_FILE).display());
216 },
217 None => println!("no signing key: this repository writes unsigned operations, and \
218 `ore key` is how it stops"),
219 }
220 Ok(())
221}
222
223/// `ore key` -- mints this replica's signing key, or replaces the one it has.
224///
225/// The public key is printed because it is the thing worth writing down: it is
226/// what another replica needs in order to know this one's work when it sees it,
227/// and a sync hands it over automatically only to repositories this one actually
228/// meets.
229///
230/// A rotation leaves the old public key in the configuration on purpose.
231/// Operations signed under it are in the history for good, and forgetting the
232/// key would turn every one of them from a verified operation into one signed by
233/// a stranger.
234///
235/// # `--veil`
236///
237/// The other key a repository may hold, and a different thing entirely: one
238/// symmetric key for the whole repository, under which every operation is
239/// encrypted before it is handed to a relay. A signing key says who wrote an
240/// operation and is published; a content key says who may read one and is never
241/// published, least of all to the relay. Given a key it installs that one, which
242/// is how a second replica joins a veiled repository; given none it mints one,
243/// and prints it, because printing it is the only way it can reach anybody.
244/// What `ore key` was asked for.
245///
246/// A repository has three kinds of key and one verb, because the verb budget is
247/// twelve and was raised for `revert` on the understanding that it was the last
248/// one. So the kind is a flag, and this is that flag with its argument attached
249/// rather than four booleans that can contradict each other.
250pub enum KeyAsk {
251 /// `ore key`: this replica's signing key.
252 Signing,
253 /// `ore key --veil [<key>]`: the repository's content key.
254 Veil(Option<String>),
255 /// `ore key --veil-key`: this replica's veil key, which receives wraps.
256 VeilKey,
257 /// `ore key --wrap <replica>`: wrap the content key for a replica whose veil
258 /// key this repository has learned.
259 Wrap(String),
260}
261
262pub fn key(repo: &mut Repo, ask: KeyAsk)
263 -> Outcome<()>
264{
265 match ask {
266 KeyAsk::Signing => (),
267 KeyAsk::Veil(given) => return veil(repo, given),
268 KeyAsk::VeilKey => return veil_key(repo),
269 KeyAsk::Wrap(who) => return wrap(repo, &who),
270 }
271 let what = res!(capture::capture(repo));
272 report(&what);
273 println!();
274 let (key, was) = res!(repo.rotate_key());
275 match was {
276 Some(old) => {
277 println!("rotated the signing key of replica {}", repo.cfg.replica);
278 println!(" was {}", old);
279 println!(" now {}", key.public_text());
280 println!("the old key stays in the configuration, so the {} operation{} \
281 signed under it still verify",
282 repo.log.len(), if repo.log.len() == 1 { "" } else { "s" });
283 },
284 None => {
285 println!("minted a signing key for replica {}", repo.cfg.replica);
286 println!(" {} {}", keys::SCHEME, key.public_text());
287 println!("operations written from here on are sealed with it; the {} \
288 already in the log stay as they were written",
289 match repo.log.len() {
290 1 => fmt!("one operation"),
291 n => fmt!("{} operations", n),
292 });
293 },
294 }
295 println!();
296 println!("the secret key is in {} and is NOT encrypted: anyone who can read that \
297 file can write operations as this replica",
298 repo.dir.join(keys::KEY_FILE).display());
299 match &repo.veil {
300 Some(_) => println!("this repository is veiled: what it sends to a relay is \
301 encrypted, and `ore key --veil` prints the key that reads it"),
302 None => (),
303 }
304 match &repo.veilkey {
305 Some(key) => println!("this replica's veil key is {}, and a content key \
306 wrapped to it reaches this machine through a relay that cannot read it",
307 key.public_text()),
308 None => println!("this replica has no veil key, so nobody can wrap a content \
309 key to it; `ore key --veil-key` mints one"),
310 }
311 Ok(())
312}
313
314/// `ore key --veil` -- the content key: mints one, installs one, or prints the
315/// one this repository holds.
316///
317/// It captures nothing and appends nothing. A content key is not history and not
318/// an operation; it decides what a relay is shown, and a repository that has just
319/// minted one is the same repository it was a moment before.
320fn veil(repo: &mut Repo, given: Option<String>)
321 -> Outcome<()>
322{
323 // With neither a key given nor one held, minting is the only thing meant. With
324 // one held and none given, printing it is: the key has to reach the person who
325 // is being let in, and this is the only place it is said out loud.
326 if given.is_none() {
327 if let Some(veil) = &repo.veil {
328 println!("this repository is veiled with {}", ore_store::veil::CIPHER);
329 println!(" {}", veil.text());
330 println!();
331 println!("hand that to a replica that is to read this repository, and it \
332 installs it with `ore key --veil <key>`. Hand it to nobody else: it is \
333 the whole of what stands between a relay and the contents.");
334 println!("it is in {} and is NOT encrypted",
335 ore_store::veil::Veil::path_of(&repo.dir).display());
336 return Ok(());
337 }
338 }
339 let bytes = match &given {
340 Some(text) => Some(res!(keys::bytes_of(text.trim()))),
341 None => None,
342 };
343 let (veil, was) = res!(repo.set_veil(bytes.as_deref()));
344 match (&given, &was) {
345 (Some(_), None) => {
346 println!("installed a content key: this repository is now veiled");
347 println!(" {}", veil.text());
348 },
349 (Some(_), Some(old)) => {
350 println!("replaced this repository's content key");
351 println!(" was {}", old);
352 println!(" now {}", veil.text());
353 },
354 (None, None) => {
355 println!("minted a content key with {}: this repository is now veiled",
356 ore_store::veil::CIPHER);
357 println!(" {}", veil.text());
358 },
359 (None, Some(old)) => {
360 println!("minted a fresh content key, replacing the one that was here");
361 println!(" was {}", old);
362 println!(" now {}", veil.text());
363 },
364 }
365 println!();
366 if was.is_some() {
367 println!("operations already on a relay were veiled under the old key and \
368 nothing here can read them under this one. Replacing a content key is not \
369 rotating a signing key: keep the old key until every replica has caught \
370 up, or push the history to a fresh repository under the new one.");
371 println!();
372 }
373 println!("from now on `ore sync <url>` encrypts every operation it hands over, \
374 and the relay carries a history it cannot read: it sees which operation \
375 follows which, and nothing either of them says.");
376 println!("the key is in {} and is NOT encrypted",
377 ore_store::veil::Veil::path_of(&repo.dir).display());
378 println!("it is never sent to a relay. A replica that is to read this repository \
379 is given it by hand.");
380 Ok(())
381}
382
383/// `ore key --veil-key` -- this replica's veil key: mints one, or shows the one
384/// it holds.
385///
386/// It captures nothing and appends nothing, for the reason [`veil`] does not: a
387/// key that decides what a relay may show this machine is not history.
388fn veil_key(repo: &mut Repo)
389 -> Outcome<()>
390{
391 let signer = match &repo.signer {
392 Some(key) => key.clone(),
393 None => return Err(err!(
394 "A veil key is published by a binding that this replica's signing key \
395 signs, and this repository holds no signing key. Run `ore key` first: \
396 without one, nothing could tell a relay that the veil key is this \
397 replica's rather than anybody's.";
398 Invalid, Configuration, Missing, Key)),
399 };
400 let (key, minted) = res!(repo.set_veilkey());
401 let binding = res!(key.binding(&signer));
402 if minted {
403 println!("minted a veil key with {} for replica {}",
404 veilkey::VEIL_KEY_SCHEME, repo.cfg.replica);
405 } else {
406 println!("replica {} already has a veil key, and it is left as it is: \
407 minting a second one would orphan every wrap already addressed to the \
408 first", repo.cfg.replica);
409 }
410 println!(" {}", key.public_text());
411 println!();
412 println!("`ore sync <url>` deposits the binding that publishes it, signed by \
413 this replica's signing key. Whoever holds the content key can then run \
414 `ore key --wrap {}` and the next sync carries the wrap back here.",
415 repo.cfg.replica);
416 println!("the binding chains: this veil key is vouched for by signing key {}, \
417 and that key's own binding is signed by itself, so a relay can check both \
418 and forge neither.", binding.signer.iter().take(4)
419 .map(|b| fmt!("{:02x}", b)).collect::<String>());
420 println!("the secret half is in {} and is NOT encrypted: anyone who can read \
421 that file can read every repository this replica has been let into",
422 repo.dir.join(veilkey::VEIL_KEY_FILE).display());
423 Ok(())
424}
425
426/// `ore key --wrap <replica>` -- wraps this repository's content key for a
427/// replica whose veil key is known here.
428///
429/// The wrap is held in the configuration and deposited by the next `ore sync
430/// <url>`, so making one is an offline act. It is public: a wrap is the content
431/// key encrypted to one veil key and is useless to everybody else, which is why
432/// it may travel through the relay the repository is being kept from.
433fn wrap(repo: &mut Repo, who: &str)
434 -> Outcome<()>
435{
436 let veil = match &repo.veil {
437 Some(v) => v.clone(),
438 None => return Err(err!(
439 "This repository is not veiled, so there is no content key to wrap. \
440 `ore key --veil` mints one; until then what goes to a relay is readable \
441 by the relay and there is nothing to let anybody into.";
442 Invalid, Configuration, Missing, Key)),
443 };
444 // A replica is printed with an `r` in front of it wherever this tool prints
445 // one, so both spellings are taken: refusing the form the tool itself prints
446 // would be a puzzle with no purpose.
447 let bare = match who.trim().strip_prefix('r') {
448 Some(rest) => rest,
449 None => who.trim(),
450 };
451 let replica: u64 = match bare.parse() {
452 Ok(n) => n,
453 Err(_) => return Err(err!(
454 "{:?} is not a replica identifier. `ore key --wrap <replica>` takes the \
455 number a replica is known by, with or without the `r` that `ore log` \
456 prints in front of it beside every operation that replica wrote.", who;
457 Invalid, Input)),
458 };
459 let known = repo.cfg.keys.clone();
460 let addressed: Vec<_> = repo.cfg.veils.iter()
461 .filter(|b| b.replica == replica && b.is_chained(&known))
462 .cloned()
463 .collect();
464 let binding = match addressed.len() {
465 1 => addressed[0].clone(),
466 0 => return Err(err!(
467 "This repository knows no veil key for replica {}, so it cannot wrap the \
468 content key to it. That replica runs `ore key --veil-key` and syncs, and \
469 this one syncs afterwards to learn what it published.", replica;
470 Invalid, Input, Missing, Key)),
471 n => return Err(err!(
472 "Replica {} has published {} veil keys and this repository cannot tell \
473 which of them to wrap to. A replica publishes one; more than one means \
474 either a key file was replaced or somebody is offering a key that is not \
475 that replica's, and either way it is worth finding out which before \
476 handing over a content key.", replica, n;
477 Invalid, Input, Mismatch, Key)),
478 };
479 let secret = res!(keys::bytes_of(&veil.text()));
480 let wrap = res!(Wrap::mint(&binding.public, &secret));
481 repo.cfg.hold_wrap(wrap);
482 res!(repo.save_config());
483 println!("wrapped this repository\'s content key for replica {}", replica);
484 println!(" to {}", keys::text_of(&binding.public));
485 println!();
486 println!("`ore sync <url>` deposits it. The relay serves it to anybody who may \
487 pull and that is not a leak: without replica {}\'s veil secret, which never \
488 leaves that machine, a wrap is nothing.", replica);
489 println!("replica {} takes it on its own next sync and is veiled from that \
490 moment. Operations it took before that were encrypted under this key too, \
491 so it can read the whole history and not only what follows.", replica);
492 Ok(())
493}
494
495/// `ore mark` -- captures and names this point in history.
496///
497/// The mark is an operation like any other and lands in the log, and every verb
498/// that reads state renders from the log, so a mark costs what it says and
499/// nothing else. It used to write a materialised copy of the whole working tree
500/// beside the log, to spare a later reader the render. That is gone: a render
501/// costs a quarter of a second, and the copy cost a working tree of disk, most
502/// of a working tree of memory whenever it was read, and went stale the moment
503/// anything was appended.
504///
505/// # What may be said about it
506///
507/// `body` is the message: whatever followed the name on the command line, as
508/// bytes, or nothing where nothing was said. Bytes and not a string, because the
509/// history does not decode what somebody wrote about their own work, any more
510/// than [`Op::Note`] does.
511///
512/// # And what the name may not be
513///
514/// A name beginning with [`repo::AUTO_MARK_PREFIX`] is refused, that being how the
515/// mark at the end of every appending command is named and how `ore back` tells
516/// the two apart.
517pub fn mark(repo: &mut Repo, name: &str, body: Option<Vec<u8>>)
518 -> Outcome<()>
519{
520 // Before the capture, because this is a fault in the argument rather than
521 // anything about the repository: somebody who mistyped a name should not have
522 // to read a capture report to find that out.
523 if repo::is_auto_mark(name) {
524 return Err(err!(
525 "A mark cannot be called {:?}. A name beginning with {:?} is one this tool \
526 wrote itself: every command that appends anything ends by naming that point \
527 after the time, so that the git mirror stays cheap to build, and `ore back` \
528 offers the points a person named by telling the two apart on exactly that \
529 character. Choose a name beginning with something else.",
530 name, repo::AUTO_MARK_PREFIX;
531 Invalid, Input, Conflict));
532 }
533 let what = res!(capture::capture(repo));
534 report(&what);
535 let replica = repo.cfg.replica;
536 let said = body.is_some();
537 let id = res!(repo.author(replica, Op::Mark {
538 name: fmt!("{}", name),
539 body,
540 time: Some(res!(repo::now_secs())),
541 }));
542 println!("mark {:?} is {}{}", name, id,
543 if said { ", and what was said about it is in the history with it" } else { "" });
544 Ok(())
545}
546
547/// `ore log` -- the history, newest first.
548///
549/// The capture, and then the listing. What is printed comes from
550/// [`Listing::of`], which is also what a listing read off the disk is, so the
551/// answer is the same answer whether the log was read or the reading was: see
552/// [`crate::listing`], and [`recount`], which is the one place either is printed.
553pub fn log(repo: &mut Repo, auto: bool)
554 -> Outcome<()>
555{
556 let what = res!(capture::capture(repo));
557 report(&what);
558 println!();
559 let listing = Listing::of(repo);
560 recount(&listing, &repo.root, repo.cfg.replica, auto)
561}
562
563/// Prints a history listing, whether it was read from the log or from the
564/// reading of it left beside the log.
565///
566/// # Which marks are shown, and why not all of them
567///
568/// Every command that appends anything ends by naming the point it reached, so a
569/// week's work leaves several hundred marks nobody chose. Listing those beside
570/// the handful somebody did is not merely long: it buries the ones a reader came
571/// for, and the count each carries stops meaning anything, because the operations
572/// "since the previous mark" are then the operations since the last *command* --
573/// which is nearly always one or two. A release with forty commands of work
574/// behind it reported nothing before it at all.
575///
576/// So the default lists the marks a person named, counts the work between them
577/// without counting the bookkeeping, and says how many commands that work took.
578/// `--auto` lists every mark there is, which is what somebody looking for a
579/// recovery point wants and nobody else does. It is the same distinction
580/// `ore back` makes when it offers the points a person named.
581pub fn recount(listing: &Listing, root: &Path, replica: ReplicaId, auto: bool)
582 -> Outcome<()>
583{
584 println!("{} operation{} in {} on replica {}",
585 listing.ops,
586 if listing.ops == 1 { "" } else { "s" },
587 root.display(),
588 replica,
589 );
590 // The frontier, because it is what says whether two replicas hold the same
591 // history: the operation count can match by coincidence and the frontier
592 // cannot.
593 println!("frontier {}", frontier_of(&listing.frontier));
594 // What is known about who wrote them, counted rather than listed: a history
595 // of ten thousand operations is not worth ten thousand marks, and the count
596 // is what says whether this history is signed throughout.
597 println!("{}", keys::LEGEND);
598 println!(" {} signed and verified, {} signed by an unknown key, {} unsigned",
599 listing.signed, listing.unknown, listing.bare);
600 let points = &listing.marks;
601 let written = points.iter().filter(|p| p.auto).count();
602 let shown: Vec<&Point> = match auto {
603 true => points.iter().collect(),
604 false => points.iter().filter(|p| !p.auto).collect(),
605 };
606 if shown.is_empty() {
607 println!();
608 match written {
609 0 => println!("no marks yet; `ore mark <name>` names a point in this history"),
610 1 => println!("no mark has been named here; `ore mark <name>` names one, and \
611 `ore log --auto` lists the one this tool wrote at the end of a command"),
612 n => println!("no mark has been named here; `ore mark <name>` names one, and \
613 `ore log --auto` lists the {} this tool wrote at the ends of commands", n),
614 }
615 return Ok(());
616 }
617 // Said once, in a paragraph of its own rather than under the provenance it has
618 // nothing to do with, so that a reader who wonders where the rest went is told
619 // rather than left to notice. Nothing is said where there is nothing to say.
620 if !auto && written > 0 {
621 println!();
622 println!("{} mark{} named here, and {} this tool wrote at the end{} of \
623 command{} that appended something; `ore log --auto` lists those too",
624 shown.len(), if shown.len() == 1 { "" } else { "s" },
625 written,
626 if written == 1 { "" } else { "s" },
627 if written == 1 { "" } else { "s" });
628 }
629 // Work not yet under any mark this listing shows.
630 let last = match shown.last() {
631 Some(p) => p.at,
632 None => 0,
633 };
634 let trailing = res!(span(listing, last + 1, listing.ops)).0;
635 if trailing > 0 {
636 println!();
637 println!("{} operation{} since the last {}mark",
638 trailing, if trailing == 1 { "" } else { "s" },
639 if auto { "" } else { "named " });
640 }
641 for (n, point) in shown.iter().enumerate().rev() {
642 // The span back to the previous mark this listing shows, which is what a
643 // reader is comparing against. The bookkeeping in it is counted apart from
644 // the work: a mark this tool wrote is not an edit somebody made, and
645 // counting it as one is how a release with forty commands behind it came to
646 // report nothing before it at all.
647 let from = match n {
648 0 => 0,
649 _ => shown[n - 1].at + 1,
650 };
651 let (work, commands) = res!(span(listing, from, point.at));
652 println!();
653 println!("mark {:?} {} {}", point.name, point.prov.mark(), point.id);
654 println!(" parents {}", parents_of(&point.parents));
655 println!(" {} operation{} before it, {}{}",
656 work,
657 if work == 1 { "" } else { "s" },
658 match (n, auto) {
659 (0, _) => "from the start",
660 (_, true) => "since the previous mark",
661 // Named, because there are marks in between and saying "the
662 // previous mark" of a span holding forty of them would be a
663 // sentence a reader has to check.
664 (_, false) => "since the previous named mark",
665 },
666 match commands {
667 0 => fmt!(""),
668 1 => fmt!(", over one command"),
669 m => fmt!(", over {} commands", m),
670 },
671 );
672 }
673 Ok(())
674}
675
676/// Counts a stretch of the log as a reader means it: the operations somebody's
677/// work put there, and the marks this tool wrote among them.
678///
679/// The two are counted apart because they answer different questions. "How much
680/// happened here" is about the first; "how many times was this repository
681/// written to" is about the second, and adding them together answers neither.
682///
683/// A listing holds the marks and the count and not the operations between them,
684/// which is the whole of why it is 60 KB where the log is 85 MB. So the
685/// bookkeeping in a stretch is counted from the marks that fall in it, and the
686/// work is what is left over.
687fn span(listing: &Listing, from: usize, to: usize)
688 -> Outcome<(usize, usize)>
689{
690 if from > to || to > listing.ops {
691 return Err(err!(
692 "A listing of {} operations was asked what stands at {}..{}.",
693 listing.ops, from, to;
694 Bug, Invalid));
695 }
696 let commands = listing.marks.iter()
697 .filter(|p| p.auto && p.at >= from && p.at < to)
698 .count();
699 Ok((to - from - commands, commands))
700}
701
702/// What `ore flags` was asked for beyond the flags themselves.
703#[derive(Clone, Debug, Default)]
704pub struct Asked {
705 /// Show only the flags nobody has marked reviewed.
706 pub new_only: bool,
707 /// Mark reviewed every flag this names: an operation identifier, or
708 /// [`crate::reviewed::ALL`].
709 pub mark: Option<String>,
710}
711
712/// `ore flags` -- what the renderer noticed about the current state.
713pub fn flags(repo: &mut Repo, asked: &Asked)
714 -> Outcome<()>
715{
716 let mut what = res!(capture::capture(repo));
717 report(&what);
718 println!();
719 // The capture hands its render on where that render is still the
720 // present and holds everything a whole one holds, so the second
721 // render most verbs used to make is skipped.
722 let tree = match what.tree.take() {
723 Some(tree) => tree,
724 None => res!(tree::whole(&repo.log)),
725 };
726 // Marking comes before the summary, so that what is printed is the state the
727 // command leaves behind rather than the one it found.
728 if let Some(which) = &asked.mark {
729 let all = tree.repo.flags();
730 let mut seen = res!(Reviewed::read(&repo.root));
731 let marked = seen.mark(all, which);
732 res!(seen.save(repo, all));
733 let held = seen.count(all);
734 if marked == 0 && held == 0 {
735 let there = match all.len() {
736 0 => fmt!("there are no flags"),
737 1 => fmt!("there is one flag"),
738 n => fmt!("there are {} flags", n),
739 };
740 if which == reviewed::ALL {
741 println!("nothing was marked reviewed: {}", there);
742 } else {
743 println!("nothing was marked reviewed: no flag names {}, and {}",
744 which, there);
745 }
746 } else {
747 println!("marked {} flag{} reviewed, {} of {} in all",
748 marked, if marked == 1 { "" } else { "s" }, held, all.len());
749 }
750 println!();
751 }
752 summarise_with(repo, &tree, asked)
753}
754
755/// Writes what the renderer noticed about a tree, which is the whole of what
756/// `ore flags` says once the capture has been reported.
757///
758/// It is a function of its own because a sync ends by saying it too: a merge
759/// that put two people's concurrent edits into one region is the one thing about
760/// a sync a reader must not have to go and ask for.
761///
762/// It is also where the buried side of every collision is spilled, since this is
763/// the one place every verb that renders passes through. The spill is derived
764/// data and is rewritten here from scratch; see [`crate::collisions`].
765pub fn summarise(repo: &Repo, tree: &tree::Tree)
766 -> Outcome<()>
767{
768 summarise_with(repo, tree, &Asked::default())
769}
770
771/// As [`summarise`], with what `ore flags` was asked for.
772///
773/// Every flag says where its content is, on a line of its own beneath it: a flag
774/// names operations and offsets into them, and the trial found that turning
775/// those into a place in a file meant reading `ore who` alongside by hand. Where
776/// the content is dead, or buried, or in no file, that is what the line says --
777/// see [`crate::place`].
778pub fn summarise_with(repo: &Repo, tree: &tree::Tree, asked: &Asked)
779 -> Outcome<()>
780{
781 let clashes = res!(tree.layout()).clashes;
782 report_clashes(&clashes);
783 let all = tree.repo.flags();
784 // The lookup walks every run of every file, so it is built where there is a
785 // flag to put somewhere and not otherwise.
786 let placed = match all.is_empty() {
787 true => None,
788 false => Some(Where::of(tree)),
789 };
790 let seen = res!(Reviewed::read(&repo.root));
791 let held = seen.count(all);
792 let mut total = 0usize;
793 let mut shown: BTreeSet<Flag> = BTreeSet::new();
794 // Every file keeps the flags that concern it, deleted files included: a move
795 // into a deleted file is flagged against that file, and a reader who is told
796 // only about live ones is told nothing about it.
797 for file in tree.repo.files() {
798 let mut wanted: Vec<&Flag> = file.flags().iter().collect();
799 for flag in file.flags() {
800 shown.insert(flag.clone());
801 }
802 if asked.new_only {
803 wanted.retain(|f| !seen.holds(f));
804 }
805 if wanted.is_empty() {
806 continue;
807 }
808 println!("{}{}", tree::shown(file.path()),
809 if file.is_live() { "" } else { " (deleted)" });
810 for flag in wanted {
811 total += 1;
812 report_flag(flag, tree, placed.as_ref(), repo, &seen, asked);
813 }
814 }
815 // A flag naming an operation that reached no file at all belongs to the
816 // repository and to nothing in it, so it is reported on its own.
817 let mut loose: Vec<&Flag> = all.iter().filter(|f| !shown.contains(f)).collect();
818 if asked.new_only {
819 loose.retain(|f| !seen.holds(f));
820 }
821 if !loose.is_empty() {
822 println!("the repository, in no file");
823 for flag in loose {
824 total += 1;
825 report_flag(flag, tree, placed.as_ref(), repo, &seen, asked);
826 }
827 }
828 if total == 0 {
829 // A clash is not a flag: two files at one path is a fact about the
830 // repository and not a fault in it, and the renderer says nothing about it.
831 // Calling that clean without qualification would be a half truth.
832 let live = tree.live().len();
833 if asked.new_only && !all.is_empty() {
834 println!("nothing new: all {} flag{} {} marked reviewed",
835 all.len(), if all.len() == 1 { "" } else { "s" },
836 if all.len() == 1 { "is" } else { "are" });
837 } else if clashes.is_empty() {
838 println!("clean: {} file{}, nothing flagged",
839 live, if live == 1 { "" } else { "s" });
840 } else {
841 println!("nothing flagged in {} file{}, and {} path{} claimed by more than one",
842 live, if live == 1 { "" } else { "s" },
843 clashes.len(), if clashes.len() == 1 { "" } else { "s" });
844 }
845 } else {
846 println!();
847 if asked.new_only {
848 println!("{} new flag{}, and {} marked reviewed",
849 total, if total == 1 { "" } else { "s" }, held);
850 } else {
851 println!("{} flag{} in all{}", total, if total == 1 { "" } else { "s" },
852 match held {
853 0 => fmt!(""),
854 n => fmt!(", {} of them marked reviewed; `ore flags --new` shows \
855 only the rest", n),
856 });
857 }
858 }
859 // The buried side of every collision, written out so that a reviewer with no
860 // forge has `diff` and needs nothing else.
861 collisions::report(&res!(collisions::spill(repo, tree)));
862 say_unswept(&repo.root.join(repo::ORE_DIR));
863 Ok(())
864}
865
866/// Says how long it has been since anything read the whole store the slow way,
867/// where that is longer than a verdict lasts.
868///
869/// **This is the one place a person is told the sweep is not running**, and it
870/// is `ore flags` because that verb's whole question is whether anything is
871/// wrong here. Every other verb says nothing, since a line printed on every
872/// command is a line nobody reads -- and `ore mark` now runs on every commit in
873/// every tree on this machine.
874///
875/// It is not an alarm. A store nobody sweeps is still checked, because a verdict
876/// goes off and the next command pays for a full read; see
877/// [`ore_store::verdict`]. What the line says is that the paying is happening in
878/// front of somebody instead of on a timer.
879fn say_unswept(dir: &Path) {
880 let at = verdict::now();
881 let last = sweep::Swept::read(dir);
882 let days = match &last {
883 Some(swept) => match swept.age(at) {
884 // A record from the future is a clock that moved, and the honest
885 // thing to say about it is nothing.
886 None => return,
887 Some(age) => match age <= VERDICT_LIFE {
888 true => return,
889 false => Some(age / (24 * 60 * 60 * 1_000_000_000)),
890 },
891 },
892 None => None,
893 };
894 // A store with nothing sealed has nothing a sweep would look at, so a repo
895 // somebody made a minute ago is left alone.
896 match Verdicts::read(dir).is_empty() {
897 true => return,
898 false => (),
899 }
900 match days {
901 Some(n) => println!("the whole log was last read and checked {} day{} ago; \
902 `ore repack --verify` does it now", n, if n == 1 { "" } else { "s" }),
903 None => println!("nothing has ever read this whole log and checked it; \
904 `ore repack --verify` does that, and a timer is what keeps doing it"),
905 }
906}
907
908/// Writes one flag: what it is, and where its content is.
909fn report_flag(
910 flag: &Flag,
911 tree: &tree::Tree,
912 placed: Option<&Where>,
913 repo: &Repo,
914 seen: &Reviewed,
915 asked: &Asked,
916)
917{
918 // A reviewed mark is worth saying only where the reviewed ones are being
919 // shown, which is everywhere but `--new`.
920 println!(" {}{}", describe(flag, &tree.repo).line(),
921 if !asked.new_only && seen.holds(flag) { " (reviewed)" } else { "" });
922 if let Some(placed) = placed {
923 println!(" {}", placed.of_flag(flag, &repo.log));
924 }
925}
926
927/// Naming a file for a message and wording a flag are both what a reader is
928/// shown rather than what the renderer computed, and the forge shows the same
929/// things, so they live in the shared crate and are re-exported here under the
930/// names the rest of this tool already calls them by.
931pub use ore_store::say::{
932 describe,
933 name_of,
934};
935
936/// `ore who` -- which operation, and so which replica, wrote each stretch of a
937/// file.
938///
939/// # Where the identity is not the whole answer
940///
941/// A stretch of a file is normally the work of the operation that names it, and
942/// that operation's author wrote those bytes. There is one case where the two
943/// part company, and it is the case in which getting it wrong would matter most:
944/// content a revert put back.
945///
946/// Nothing in this system un-buries. Deleted content stays deleted -- the
947/// tombstone set is grow-only and is rebuilt from the operations on every render
948/// -- so a revert that restores text can only insert a **copy**, under a new
949/// identity, authored by whoever ran the revert. Reported plainly, that would
950/// credit the reverter with somebody else's writing.
951///
952/// So it is not reported plainly. [`crate::revert::Restored`] reads the
953/// [`Op::Reverts`] record back: the record says which operation was undone, that
954/// operation says what content it removed, and that content says who wrote it.
955/// Both facts are then shown -- who put the bytes back, and who wrote them first
956/// -- because both are true and neither on its own is the answer.
957pub fn who(repo: &mut Repo, path: &str)
958 -> Outcome<()>
959{
960 let mut what = res!(capture::capture(repo));
961 report(&what);
962 println!();
963 // The capture hands its render on where that render is still the
964 // present and holds everything a whole one holds, so the second
965 // render most verbs used to make is skipped.
966 let tree = match what.tree.take() {
967 Some(tree) => tree,
968 None => res!(tree::whole(&repo.log)),
969 };
970 // The working copy's names are what a person has in front of them, so a file
971 // a clash sent to a derived name is asked for under that name.
972 let layout = res!(tree.layout());
973 let want = path.as_bytes();
974 let file = match layout.file_at(want).and_then(|id| tree.get(id)) {
975 Some(f) => f,
976 None => {
977 let known: Vec<String> = layout.at.keys().map(|p| tree::shown(p)).collect();
978 return Err(err!(
979 "The repository holds no file {:?}. It holds: {}.",
980 path,
981 if known.is_empty() { fmt!("nothing") } else { known.join(", ") };
982 Invalid, Input, NotFound));
983 },
984 };
985 println!("{} {} byte{} in {} run{}",
986 path,
987 file.len(), if file.len() == 1 { "" } else { "s" },
988 file.runs().len(), if file.runs().len() == 1 { "" } else { "s" },
989 );
990 println!("created by {}", file.file());
991 if file.path() != want {
992 println!("recorded at {}, and written here because two files hold that path",
993 tree::shown(file.path()));
994 }
995 println!("{}", keys::LEGEND);
996 // Built once, from the log, and only where the history holds a revert at all:
997 // a repository nobody has reverted anything in pays one pass over its records
998 // and prints nothing extra.
999 let restored = revert::Restored::of(&repo.log);
1000 if !restored.is_empty() {
1001 println!("a run this history put back is shown twice over: the operation that \
1002 holds the bytes now, and the one that wrote them first");
1003 }
1004 println!();
1005 let bytes = file.bytes();
1006 for run in file.runs() {
1007 let from = run.at as usize;
1008 let to = from + run.content.len() as usize;
1009 // The mark goes after the byte range and before the operation, so that a
1010 // reader's eye finds the range where it always was and the provenance
1011 // beside the name it belongs to.
1012 println!("{:>8}..{:<8} {} {:<14} {}",
1013 from,
1014 to,
1015 mark_of(&repo.prov, run.content.op()),
1016 fmt!("{}", run.content.op()),
1017 show(&bytes[from..to], SHOW_LIMIT),
1018 );
1019 // And where these bytes are a copy a revert put back, whose writing they
1020 // are. The identity above is the copy's and is honestly the copy's: what
1021 // this line adds is the author the copy would otherwise have taken the
1022 // credit from.
1023 if let Some((record, was)) = restored.of_op(&run.content.op()) {
1024 println!("{:>18} restored by {}, and first written by {} {}",
1025 "",
1026 record,
1027 mark_of(&repo.prov, was.op()),
1028 fmt!("{}", was.op()),
1029 );
1030 }
1031 }
1032 // Built once where there is anything to place, since building it walks every
1033 // run of every file, and not at all where there is not.
1034 if !file.flags().is_empty() || !file.notes().is_empty() {
1035 let placed = Where::of(&tree);
1036 if !file.flags().is_empty() {
1037 println!();
1038 for flag in file.flags() {
1039 println!(" {}", describe(flag, &tree.repo).line());
1040 println!(" {}", placed.of_flag(flag, &repo.log));
1041 }
1042 }
1043 // What people have said about this file's content, beneath what the
1044 // renderer noticed about it: both are things a reader of the file wants,
1045 // and only one of them is a fault.
1046 if !file.notes().is_empty() {
1047 println!();
1048 for note in file.notes() {
1049 // The identity as well as the place, since it is how a note is named
1050 // to anything that asks about one.
1051 println!(" note {} {:<14} {}",
1052 mark_of(&repo.prov, note.note()),
1053 fmt!("{}", note.note()),
1054 placed.of_spans(file.file(), note.spans()),
1055 );
1056 println!(" {}", note.text_lossy());
1057 }
1058 }
1059 }
1060 Ok(())
1061}
1062
1063/// Returns the operation a mark was recorded as, or says which marks there are.
1064///
1065/// The highest of that name in operation order wins, a name being a label rather
1066/// than an identity: naming a point twice is a person saying where they are now.
1067/// Operation order carries that meaning, because a counter is one past the
1068/// highest anybody has written, so a mark made after seeing an earlier one
1069/// outranks it; and it is shared, so every replica answers alike.
1070fn find_mark(repo: &Repo, name: &str)
1071 -> Outcome<OpId>
1072{
1073 // One name can be held by more than one mark: re-marking a name moves it, and
1074 // two replicas can mark the same name concurrently without either seeing the
1075 // other. The matches are therefore arbitrated, by the same total order the
1076 // renderer uses for a path clash, and never taken in the order the log
1077 // happens to hold them. Append order is arrival order, which two replicas
1078 // holding the very same operations legitimately disagree about, so resolving
1079 // a name by it lets one mark mean two states.
1080 let found = repo.log.iter()
1081 .filter_map(|rec| match &rec.op {
1082 Op::Mark { name: got, .. } if got == name => Some(rec.head.id()),
1083 _ => None,
1084 })
1085 .max_by_key(OpOrder::of);
1086 match found {
1087 Some(id) => Ok(id),
1088 None => {
1089 // The marks a person named, and a count of the ones this tool named for
1090 // them. Every command that appends anything writes one, so a repository
1091 // worked in for a week holds hundreds, and listing those instead of the
1092 // handful somebody chose would be an offer of nothing.
1093 let mut names: Vec<String> = Vec::new();
1094 let mut automatic = 0usize;
1095 for rec in repo.log.iter() {
1096 if let Op::Mark { name, .. } = &rec.op {
1097 match repo::is_auto_mark(name) {
1098 true => automatic += 1,
1099 false => names.push(fmt!("{:?}", name)),
1100 }
1101 }
1102 }
1103 let also = match automatic {
1104 0 => fmt!(""),
1105 n => fmt!(" Beside them are {} written by this tool, one for each command \
1106 that appended anything, each named for the time it was written.", n),
1107 };
1108 Err(err!(
1109 "No mark is called {:?}. The marks are: {}.{}",
1110 name,
1111 if names.is_empty() { fmt!("none yet") } else { names.join(", ") },
1112 also;
1113 Invalid, Input, NotFound))
1114 },
1115 }
1116}
1117
1118/// Writes the mark that ends every command that appended operations, and returns
1119/// it, or nothing where the command had already ended on one.
1120///
1121/// # Why every command, and not the ones that seem to deserve it
1122///
1123/// The git mirror has a cheap path and an expensive one: where the frontier is
1124/// exactly one mark it moves the branch to that mark's commit, and otherwise it
1125/// renders the whole tree and makes a commit for the work no mark names. The
1126/// cheap path can only be entered from itself, so the first command that appends
1127/// operations and does not end on a mark costs the expensive path on every
1128/// marking command thereafter, for the life of the repository -- measured on
1129/// fe2o3 at 2.84 seconds against 1.44. That is why this is not a judgement about
1130/// which commands are worth marking: one lapse is permanent, so there are no
1131/// exceptions, `sync`, `undo` and `back` included.
1132///
1133/// A mark authored against the whole frontier covers every head it merges, so two
1134/// replicas that diverged and merged are back on the cheap path with one
1135/// operation between them.
1136///
1137/// # It is history and not bookkeeping
1138///
1139/// The mark is a real operation, signed and synced like any other. It is not
1140/// driven from `.ore/batches`, which is this working copy's own memory and may be
1141/// deleted without consequence; the mirror has to stay a function of the log.
1142pub fn auto_mark(repo: &mut Repo)
1143 -> Outcome<Option<OpId>>
1144{
1145 // A command that ended on a mark of its own -- `ore mark` -- has nothing to
1146 // add, and a second mark on the same point would say the same thing twice.
1147 let frontier = repo.log.frontier();
1148 if let [only] = frontier[..] {
1149 if let Some(rec) = repo.log.get(&only) {
1150 if matches!(rec.op, Op::Mark { .. }) {
1151 return Ok(None);
1152 }
1153 }
1154 }
1155 // One reading, spelled twice: the name carries the microsecond that keeps two
1156 // of these apart, and the operation carries the second the wire holds. Reading
1157 // the clock again for the second would let the two disagree.
1158 let micros = res!(repo::now_micros());
1159 let replica = repo.cfg.replica;
1160 let id = res!(repo.author(replica, Op::Mark {
1161 name: repo::auto_mark_name(micros),
1162 body: None,
1163 time: Some(micros / 1_000_000),
1164 }));
1165 Ok(Some(id))
1166}
1167
1168/// Writes what bringing the git mirror current amounted to.
1169///
1170/// A run with nothing to do says nothing: a mirror that is already right is the
1171/// ordinary case, and a line announcing it every time is a line nobody reads.
1172pub fn report_mirror(report: &crate::mirror::Report) {
1173 if report.is_empty() {
1174 return;
1175 }
1176 let mut said: Vec<String> = Vec::new();
1177 if report.commits > 0 {
1178 said.push(fmt!("{} commit{}",
1179 report.commits, if report.commits == 1 { "" } else { "s" }));
1180 }
1181 if report.tags > 0 {
1182 said.push(fmt!("{} tag{}",
1183 report.tags, if report.tags == 1 { "" } else { "s" }));
1184 }
1185 if report.tail {
1186 said.push(fmt!("a commit for work no mark names yet"));
1187 }
1188 println!("git mirror {}: {}", report.path.display(),
1189 if said.is_empty() { fmt!("brought current") } else { said.join(", ") });
1190}
1191
1192/// Writes what materialising a state did to the working copy.
1193fn report_moved(moved: &tree::Moved) {
1194 report_clashes(&moved.clashes);
1195 report_moved_files(moved);
1196}
1197
1198/// Writes which files materialising a state touched, saying nothing about the
1199/// clashes.
1200///
1201/// A sync reports the clashes in the summary it ends with, which is where a
1202/// reader of `ore flags` expects to find them, so it says them once rather than
1203/// twice.
1204pub fn report_moved_files(moved: &tree::Moved) {
1205 if moved.is_empty() {
1206 println!("nothing moved; it was already that state");
1207 return;
1208 }
1209 for path in &moved.created {
1210 println!(" created {}", tree::shown(path));
1211 }
1212 for path in &moved.updated {
1213 println!(" updated {}", tree::shown(path));
1214 }
1215 for path in &moved.removed {
1216 println!(" removed {}", tree::shown(path));
1217 }
1218 println!("{} file{} left alone", moved.unchanged,
1219 if moved.unchanged == 1 { "" } else { "s" });
1220}
1221
1222/// `ore back` -- puts the working copy back to the state a mark named.
1223///
1224/// The history is untouched. The capture that runs first records whatever the
1225/// working copy held, so the state being left is in the log before the state
1226/// being visited is written out, and the log only ever grows.
1227///
1228/// What `back` does not do is record the arrival. The working copy is left
1229/// standing at an old state that the history does not yet say it is at, and the
1230/// next verb captures it. [`undo`] is the same journey with that capture done
1231/// at once: `back` visits, `undo` commits the visit.
1232pub fn back(repo: &mut Repo, name: &str)
1233 -> Outcome<()>
1234{
1235 let what = res!(capture::capture(repo));
1236 report(&what);
1237 let id = res!(find_mark(repo, name));
1238 let tree = res!(tree::at(&repo.log, &[id]));
1239 let moved = res!(tree::materialise(&repo.root, &tree, tree::Surplus::Remove));
1240 println!();
1241 println!("the working copy is now the state at mark {:?}, {}", name, id);
1242 report_moved(&moved);
1243 Ok(())
1244}
1245
1246/// `ore undo` -- puts back an earlier state and records the restoration as new
1247/// operations.
1248///
1249/// # Undo is an edit, not a rewind
1250///
1251/// Nothing is removed from the history, because nothing in this system ever is.
1252/// An undo renders an earlier state, writes it into the working copy and then
1253/// captures it, so what reaches the log is the ordinary difference between
1254/// where the working copy stood and where it now stands. Both states remain,
1255/// the operations that made the undone change remain, and `ore who` still names
1256/// whoever wrote them.
1257///
1258/// # What "the last thing" means
1259///
1260/// With no argument, `undo` puts back the state from before the last command
1261/// this working copy ran that appended anything -- or, if the capture at the
1262/// start of this very command found changes nobody had recorded, from before
1263/// those. The commands are read from the batch record described in
1264/// [`crate::repo`], which is the tool's own memory and not part of the history.
1265///
1266/// With a mark, `undo` puts back the state that mark names. That is
1267/// [`back`] followed by a capture, and the difference between the two is
1268/// exactly that: `back` visits an old state and leaves the next verb to notice,
1269/// `undo` records the arrival itself.
1270///
1271/// # What an undone deletion comes back as
1272///
1273/// The bytes come back; the identity does not. A file the working copy no
1274/// longer holds is written out again, and the capture that follows records what
1275/// it sees, which is a file that was not there before: a create and a splice.
1276/// The vocabulary has no operation that revives a deleted file, so the restored
1277/// file is a new file with the old contents, and `ore who` dates its provenance
1278/// from the undo. Undoing an edit keeps every byte's author; undoing a deletion
1279/// does not.
1280///
1281/// # Undoing twice is redoing
1282///
1283/// An undo is a command that appended operations, so it is a batch like any
1284/// other. Undoing again therefore puts back the state from before the undo,
1285/// which is the state the undo took away. A third undo returns to the first
1286/// one's state, and so on: with the history only growing, undo and redo are the
1287/// same verb run twice.
1288pub fn undo(repo: &mut Repo, mark: Option<&str>)
1289 -> Outcome<()>
1290{
1291 // The frontier before this command captured anything, which is where an
1292 // undo of changes nobody had recorded yet has to return to.
1293 let started = repo.log.frontier();
1294 let what = res!(capture::capture(repo));
1295 report(&what);
1296 let (told, frontier) = match mark {
1297 Some(name) => {
1298 let id = res!(find_mark(repo, name));
1299 (fmt!("the state at mark {:?}, {}", name, id), vec![id])
1300 },
1301 None if !what.is_empty() => (
1302 fmt!("the state from before the {} operation{} just captured",
1303 what.ops, if what.ops == 1 { "" } else { "s" }),
1304 started,
1305 ),
1306 None => match repo.batches.last() {
1307 Some(batch) => (
1308 fmt!("the state from before `ore {}`", batch.verb),
1309 batch.before.clone(),
1310 ),
1311 None => {
1312 println!();
1313 println!("there is nothing to undo: no command of this working copy has \
1314 appended anything, and the working copy is what the history says");
1315 return Ok(());
1316 },
1317 },
1318 };
1319 let tree = res!(tree::at(&repo.log, &frontier));
1320 let moved = res!(tree::materialise(&repo.root, &tree, tree::Surplus::Remove));
1321 println!();
1322 println!("the working copy is now {}", told);
1323 report_moved(&moved);
1324 // And the arrival is recorded, which is what makes this an undo rather than
1325 // a visit. The operations are the difference between the two states, so
1326 // undoing a small change costs a small change.
1327 println!();
1328 let back = res!(capture::capture(repo));
1329 report(&back);
1330 Ok(())
1331}
1332
1333
1334/// Rewrites the container of the repository at `root`, keeping every operation
1335/// and every signature exactly as they are.
1336///
1337/// The one command here that does not begin by capturing the working copy.
1338/// Every other verb opens a [`Repo`], which appends whatever the working copy
1339/// has changed; a maintenance command that wrote an operation into the history
1340/// it was about to rewrite would be the worst possible thing for this of all
1341/// commands to do. So the configuration is read for its trust set and the
1342/// repository is not opened at all.
1343///
1344/// `unverified` is what a relay does: it holds no keys, it is not an authority,
1345/// and it may be carrying veiled operations that nothing here could check even
1346/// if it wanted to.
1347/// `ore repack --verify`: reads every segment the slow way and files fresh
1348/// verdicts, which is the scheduled half of the bargain
1349/// [`oxedyne_fe2o3_ore::segment::Integrity::Vouched`] struck.
1350///
1351/// It writes nothing to the log and moves no byte of a segment. What it changes
1352/// is what the next command is willing to skip.
1353pub fn sweep(root: &Path, unverified: bool)
1354 -> Outcome<()>
1355{
1356 let root = res!(Repo::find_root(root));
1357 let dir = root.join(repo::ORE_DIR);
1358 let cfg = res!(repo::Config::read(&dir));
1359 let trust = cfg.trust();
1360 let how = match unverified {
1361 true => Verify::Nothing,
1362 false => Verify::Signatures(&trust),
1363 };
1364 // What the last one found, before this one replaces it. A sweep that has to
1365 // be told how long it has been since the last is a sweep nobody scheduled.
1366 match sweep::Swept::read(&dir) {
1367 Some(was) => println!("last checked {} ago, {} segments and {} bytes, {}",
1368 said_age(was.age(verdict::now())),
1369 was.segments, was.bytes,
1370 match was.rotten.len() {
1371 0 => fmt!("all of them sound"),
1372 1 => fmt!("one of them damaged"),
1373 n => fmt!("{} of them damaged", n),
1374 }),
1375 None => println!("nothing has checked this log before, or the record of it \
1376 is not one this build reads"),
1377 }
1378 println!("checking {:?}, every record and every signature, on no verdict", root);
1379 let done = res!(sweep::verify(&dir, how));
1380 println!("{} segment{} read, {} bytes", done.segments,
1381 if done.segments == 1 { "" } else { "s" }, done.bytes);
1382 if done.rotten.is_empty() {
1383 match unverified {
1384 true => println!("every record holds its digest; no signature was \
1385 checked, because none was asked for"),
1386 false => println!("every record holds its digest and every signature \
1387 verified"),
1388 }
1389 return Ok(());
1390 }
1391 // The whole reason the sweep exists, so it is said at length rather than
1392 // counted. Nothing here can repair a segment; what has happened is that it
1393 // has lost its verdict, and the next verb to touch it will refuse.
1394 println!();
1395 for rot in &done.rotten {
1396 println!("{} is damaged", rot.name);
1397 println!(" {}", rot.said);
1398 }
1399 println!();
1400 println!("no verdict was left for {}, so the next command that reads the log \
1401 will check {} again, fail, and name the operation. The bytes are gone and \
1402 nothing here can put them back: recover {} from a backup, or from a replica \
1403 that still holds the same operations, and run this again.",
1404 match done.rotten.len() {
1405 1 => fmt!("it"),
1406 _ => fmt!("them"),
1407 },
1408 match done.rotten.len() {
1409 1 => fmt!("it"),
1410 _ => fmt!("them"),
1411 },
1412 match done.rotten.len() {
1413 1 => fmt!("the file"),
1414 _ => fmt!("those files"),
1415 });
1416 Err(err!(
1417 "{} segment{} of {} in {:?} did not hold: {}.",
1418 done.rotten.len(), if done.rotten.len() == 1 { "" } else { "s" },
1419 done.segments, root,
1420 done.rotten.iter().map(|r| r.name.clone()).collect::<Vec<_>>().join(", ");
1421 Data, Checksum, Mismatch))
1422}
1423
1424/// How long ago, in the largest unit that does not round it to nothing.
1425fn said_age(age: Option<u128>) -> String {
1426 const HOUR: u128 = 60 * 60 * 1_000_000_000;
1427 match age {
1428 None => fmt!("some time in the future, by this clock"),
1429 Some(n) if n < HOUR => fmt!("under an hour"),
1430 Some(n) if n < 24 * HOUR => {
1431 let h = n / HOUR;
1432 fmt!("{} hour{}", h, if h == 1 { "" } else { "s" })
1433 },
1434 Some(n) => {
1435 let d = n / (24 * HOUR);
1436 fmt!("{} day{}", d, if d == 1 { "" } else { "s" })
1437 },
1438 }
1439}
1440
1441pub fn repack(root: &Path, unverified: bool, plan: &repack::Plan)
1442 -> Outcome<()>
1443{
1444 let root = res!(Repo::find_root(root));
1445 let dir = root.join(repo::ORE_DIR);
1446 let cfg = res!(repo::Config::read(&dir));
1447 let trust = cfg.trust();
1448 let how = match unverified {
1449 true => Verify::Nothing,
1450 false => Verify::Signatures(&trust),
1451 };
1452 println!("repacking {:?}, filling {} bytes of records a segment, {}",
1453 root, plan.segment_bytes,
1454 match plan.packing {
1455 Packing::Packed => "packing them together",
1456 Packing::Plain => "each record framed on its own",
1457 });
1458 let done = res!(repack::repack(&dir, how, plan));
1459 println!("{} operations kept: {} signed, {} unsigned, {} veiled",
1460 done.operations, done.sealed, done.bare, done.veiled);
1461 println!("{} segments of {} bytes became {} of {}{}",
1462 done.segments_before, done.bytes_before, done.segments_after, done.bytes_after,
1463 match done.bytes_before {
1464 0 => fmt!(""),
1465 n => fmt!(" ({:.1}%)", done.bytes_after as f64 * 100.0 / n as f64),
1466 });
1467 println!("shape {:02x?} before and after, so the same operations are sealed the \
1468 same way in the same order", &done.shape[..8]);
1469 match unverified {
1470 true => println!("no signature was checked, because none was asked for"),
1471 false => println!("every signature verified, on the way out and on the way back"),
1472 }
1473 Ok(())
1474}