Re-implement change printing and parsing
[?]
FHRXP5Jnb2MWLDrPrnLnkN2ryWcGCo6CRr1dXR9FW2YA
Sep 12, 2021, 7:01 PM
5FI6SBEZ6RERERUAIWQJVAY66BEZ7YQOYOUNK2DPOLRGS2X326RACDependencies
- [2]
RUBSM5DRFixing a bug when outputting changes in text format - [3]
YDMAIJ5VFixing the format of text changes (file additions under a new directory were not always accepted by the parser) - [4]
V4T4SC7OTesting binary diff - [5]
LWBBN2IBFixing a bug with double-quotes in the text format serialisation - [6]
IUH7IMWEContributor license agreements - [7]
YN63NUZOSanakirja 1.0 - [8]
X243Z3Y5Recording only the required metadata (can even be changed later!) - [9]
VO5OQW4WRemoving anyhow in libpijul - [10]
EAZ45JTFFixing a bug in change printing - [11]
6HNRL5RTdetect non-utf8 text files - [12]
PDTUHOMVfix left over conflicts - [13]
246V5TYIdecode existing files - [14]
TVVW53HZConflict resolution - [15]
VMOYG7MKtext file decoding for new files - [16]
SGXOEWHUAdding a patched chardetng (temporarily) - [17]
FXEDPLRIResurrecting tests, and type cleanup (no need for Arc<RwLock<…>> anymore) - [18]
KJDQ2WOMFixing the parsing of section headers in the text change format - [19]
6YMDOZIBRefactoring apply - [20]
RRCSHAYZFormatting - [21]
73NW2X2MReturning a parse error instead of panicking when parsing a text change - [22]
NE4A4WUKParsing of file addition metadata - [23]
NG3Z3DOKroundtrip text encoding when recording - [24]
CFNFIUJVParsing the correct flags for new vertices - [25]
ZSF3YFZTencoded file deletion - [26]
YTQS4ES3Fixing a parsing problem (related to permissions), and the associated permissions - [27]
LV34DUJYFormatting - [28]
KL5737GRText format: correct position of encoding for deletion/undeletion - [29]
65S67T3EParsing \\ for empty additions (rare problem) - [30]
NYOF5766track file encoding in the record, including change text for file adds - [31]
XSEODPNEFixing conflicts - [32]
6CZYYOG7Faster guessing of encoding - [33]
CCFJ7VO3Renaming "Record" to "Hunk" in the changes - [34]
3S6LU2U5abstract out FileMetadata (de)serialistion - [35]
UM5DLRPBstore new non-UTF-8 files raw and decode to deplay the contents - [36]
CIEUBH46Fixing an index-out-of-bounds error when serialising bad changes - [37]
BZCGXVS7Fixing two bugs around conflicts on the last line, where invalid patches were produced (first bug) and applied (second bug) - [38]
CCLLB7OIUpgrading to Sanakirja 0.15 + version bump - [39]
VYHHOEYHVersions and formatting - [40]
IIV3EL2XCleanup, formatting, and fixing the Git feature - [41]
XR7MNOMUfile encoding in updates - [42]
4DNDMC7IFixing a number of bugs related to encodings (extra newlines + misdetection in linux2x) - [43]
SFJ3XRTFProper escaping of UTF-8 filenames in the patch text format - [44]
ZRUPLBBTColours in diff and change: separating concerns and dependencies - [45]
EQLDTLXVFixing a bug with empty new files, and another one with empty replacements - [46]
I24UEJQLVarious post-fire fixes - [47]
Q3GU26WDmerge with changes from sanakirja v1.1.2 - [*]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [*]
IACED7RWtext_encoding module - [*]
SHSJ3Y53Fixing more tests - [*]
FYUDBQ3CFormatting changes + version bump - [*]
TNN56XYKlibpijul alpha.43 - [*]
QJXNUQFJSolving conflicts
Change contents
- replacement in libpijul/src/working_copy/mod.rs at line 31
let (encoding, score) = detector.guess_assess(None, true);if score {Ok(Some(Encoding(encoding)))if let Some(e) = detector.get_valid(None, true, &buffer[init..]) {Ok(Some(Encoding(e))) - edit in libpijul/src/text_encoding.rs at line 5
#[cfg(test)]use quickcheck::{Arbitrary, Gen}; - edit in libpijul/src/text_encoding.rs at line 44
#[cfg(test)]// TODO: more encodings. This is not critical, since it is not used ATM.// But the instance is needed.impl Arbitrary for Encoding {fn arbitrary(g: &mut Gen) -> Self {g.choose(&[Encoding(encoding_rs::UTF_8),Encoding(encoding_rs::SHIFT_JIS),]).unwrap().clone()}} - file addition: text_changes.rs[49.248792]
use crate::change::*;use crate::changestore::*;use crate::pristine::*;use crate::record::*;use crate::working_copy::*;use crate::*;use std::io::Write;use super::*;fn hash_mismatch(change: &Change) -> Result<(), anyhow::Error> {env_logger::try_init().unwrap_or(());use crate::change::*;let mut buf = tempfile::NamedTempFile::new()?;let mut h = change.serialize(&mut buf)?;match h {crate::pristine::Hash::Blake3(ref mut h) => h[0] = h[0].wrapping_add(1),_ => unreachable!(),}match Change::deserialize(buf.path().to_str().unwrap(), Some(&h)) {Err(ChangeError::ChangeHashMismatch { .. }) => {}_ => unreachable!(),}let f = ChangeFile::open(h, buf.path().to_str().unwrap())?;assert_eq!(f.hashed(), &change.hashed);Ok(())}#[test]fn hash_mism() -> Result<(), anyhow::Error> {env_logger::try_init().unwrap_or(());let contents = b"a\nb\nc\nd\ne\nf\n";let repo = working_copy::memory::Memory::new();let store = changestore::memory::Memory::new();repo.add_file("file", contents.to_vec());repo.add_file("file2", contents.to_vec());let env = pristine::sanakirja::Pristine::new_anon()?;let txn = env.arc_txn_begin().unwrap();let mut channel = txn.write().open_or_create_channel("main")?;txn.write().add_file("file", 0)?;txn.write().add_file("file2", 0)?;let mut state = Builder::new();state.record(txn.clone(),Algorithm::Myers,channel.clone(),&repo,&store,"",0,).unwrap();let rec = state.finish();let changes: Vec<_> = rec.actions.into_iter().map(|rec| rec.globalize(&*txn.read()).unwrap()).collect();info!("changes = {:?}", changes);let change0 = crate::change::Change::make_change(&*txn.read(),&channel,changes,std::mem::take(&mut *rec.contents.lock()),crate::change::ChangeHeader {message: "test".to_string(),authors: vec![],description: None,timestamp: chrono::Utc::now(),},Vec::new(),).unwrap();let hash0 = store.save_change(&change0)?;apply::apply_local_change(&mut *txn.write(),&mut channel,&change0,&hash0,&rec.updatables,)?;hash_mismatch(&change0)?;Ok(())}#[cfg(feature = "text-changes")]#[test]fn text_changes() -> Result<(), anyhow::Error> {env_logger::try_init().unwrap_or(());let contents = b"a\nb\nc\nd\ne\nf\n";let repo = working_copy::memory::Memory::new();let store = changestore::memory::Memory::new();repo.add_file("file", contents.to_vec());repo.add_file("file2", contents.to_vec());let env = pristine::sanakirja::Pristine::new_anon()?;let txn = env.arc_txn_begin().unwrap();let channel = txn.write().open_or_create_channel("main")?;txn.write().add_file("file", 0)?;txn.write().add_file("file2", 0)?;let h0 = record_all(&repo, &store, &txn, &channel, "")?;let change0 = store.get_change(&h0).unwrap();text_test(&store, &change0, h0);write!(repo.write_file("file")?, "a\nx\nc\ne\ny\nf\n")?;let h1 = record_all(&repo, &store, &txn, &channel, "")?;let change1 = store.get_change(&h1).unwrap();text_test(&store, &change1, h1);repo.remove_path("file2", false)?;let h2 = record_all(&repo, &store, &txn, &channel, "")?;let change2 = store.get_change(&h2).unwrap();text_test(&store, &change2, h2);repo.rename("file", "file3")?;txn.write().move_file("file", "file3", 0)?;let h3 = record_all(&repo, &store, &txn, &channel, "")?;let change3 = store.get_change(&h3).unwrap();text_test(&store, &change3, h3);// name conflictslet env2 = pristine::sanakirja::Pristine::new_anon()?;let txn2 = env2.arc_txn_begin().unwrap();let channel2 = txn2.write().open_or_create_channel("main")?;let repo2 = working_copy::memory::Memory::new();apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h0)?;apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h1)?;apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h2)?;output::output_repository_no_pending(&repo2, &store, &txn2, &channel2, "", true, None, 1, 0)?;repo2.rename("file", "file4")?;txn2.write().move_file("file", "file4", 0)?;record_all(&repo2, &store, &txn2, &channel2, "")?;apply::apply_change(&store, &mut *txn2.write(), &mut *channel2.write(), &h3)?;output::output_repository_no_pending(&repo2, &store, &txn2, &channel2, "", true, None, 1, 0)?;let h = record_all(&repo2, &store, &txn2, &channel2, "")?;let solution = store.get_change(&h).unwrap();text_test(&store, &solution, h);Ok(())}fn text_test<C: ChangeStore>(c: &C, change0: &Change, h: Hash) {let mut v = Vec::new();// let channel = channel.borrow();change0.write(c, Some(h), true, &mut v).unwrap();println!("{}", String::from_utf8_lossy(&v));for i in std::str::from_utf8(&v).unwrap().lines() {debug!("{}", i);}let change1 = Change::read(std::io::Cursor::new(&v[..]), &mut HashMap::default()).unwrap();if change0.header != change1.header {error!("header: {:#?} != {:#?}", change0.header, change1.header);}if change0.dependencies != change1.dependencies {error!("deps: {:#?} != {:#?}",change0.dependencies, change1.dependencies);}if change0.extra_known != change1.extra_known {error!("extra: {:#?} != {:#?}",change0.extra_known, change1.extra_known);}if change0.metadata != change1.metadata {error!("meta: {:#?} != {:#?}", change0.metadata, change1.metadata);}if change0.changes != change1.changes {if change0.changes.len() != change1.changes.len() {trace!("change0.changes = {:#?}", change0.changes);trace!("change1.changes = {:#?}", change1.changes);} else {for (a, b) in change0.changes.iter().zip(change1.changes.iter()) {trace!("change0: {:#?}", a);trace!("change1: {:#?}", b);for (a, b) in a.iter().zip(b.iter()) {if a != b {error!("change0 -> {:#?}", a);error!("change1 -> {:#?}", b);}}}}}if change0.contents != change1.contents {error!("change0.contents = {:?}", change0.contents);error!("change1.contents = {:?}", change1.contents);}assert_eq!(change0, &change1);}quickcheck! {fn string_roundtrip(s1: String) -> () {let mut w = Vec::new();write!(&mut w, "{}", Escaped(&s1)).unwrap();let utf = std::str::from_utf8(&w).unwrap();let (_, s2) = parse_string(&utf).unwrap();assert_eq!(s1, s2);}}#[test]#[cfg(feature = "text-changes")]fn hunk_roundtrip_test() {fn go(hunk: PrintableHunk) {let mut w = Vec::new();hunk.write(&mut &mut w).unwrap();let s = std::str::from_utf8(&w).unwrap();match parse_hunk(&s) {Ok((_, hunk2)) => {if hunk != hunk2 {eprintln!("## Hunk: \n\n{}", s);eprintln!("In: {:?}\n\nOut: {:?}\n", hunk, hunk2);panic!("Hunks don't match.");}}Err(e) => {eprintln!("Hunk: \n\n{}", s);panic!("Can't parse hunk. Error: {:?}", e);}}}// Create a new thread with custom stack sizestd::thread::Builder::new()// frequently blown by shrinking :(// You can disable shrinking by commenting out the shrink function// for PrintableHunk.stack_size(20 * 1024 * 1024).spawn(move || {quickcheck::QuickCheck::new().tests(1000).gen(quickcheck::Gen::new(3)).max_tests(100000000).quickcheck(go as fn(PrintableHunk) -> ());}).unwrap().join().unwrap();} - edit in libpijul/src/tests/mod.rs at line 22
mod text_changes; - edit in libpijul/src/pristine/vertex.rs at line 3
#[cfg(test)]use quickcheck::{Arbitrary, Gen}; - edit in libpijul/src/pristine/vertex.rs at line 113
}}#[cfg(test)]impl Arbitrary for ChangePosition {fn arbitrary(g: &mut Gen) -> Self {ChangePosition(L64(u64::arbitrary(g))) - edit in libpijul/src/pristine/vertex.rs at line 121
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {Box::new(self.0 .0.shrink().map(|x| ChangePosition(L64(x))))} - edit in libpijul/src/lib.rs at line 13
#[cfg(test)]#[macro_use]extern crate quickcheck; - edit in libpijul/src/chardetng/mod.rs at line 2917
}/// Get a valid encoding. This is checked by round-tripping the encoding on `buffer`./// TODO: This is a hack. Make it better. Make it faster.pub fn get_valid(&self,tld: Option<&[u8]>,allow_utf8: bool,buffer: &[u8],) -> Option<&'static Encoding> {if let (encoding, true) = self.guess_assess(tld, allow_utf8) {if let (s, e, false) = encoding.decode(buffer) {if encoding.encode(&s).0 == buffer {return Some(e);}}}None - edit in libpijul/src/change.rs at line 11
#[cfg(feature = "text-changes")]mod parse;#[cfg(feature = "text-changes")]mod printable; - edit in libpijul/src/change.rs at line 17
pub use parse::*; // for testingpub use printable::*; // for testing - edit in libpijul/src/change/text_changes.rs at line 4
use regex::Captures; - edit in libpijul/src/change/text_changes.rs at line 6
use crate::change::parse::*;use crate::change::printable::*; - edit in libpijul/src/change/text_changes.rs at line 16
#[error(transparent)]Nom(#[from] nom::Err<nom::error::Error<String>>), - replacement in libpijul/src/change/text_changes.rs at line 169
let (mut change, extra_dependencies) = Self::read_(r, updatables)?;let (mut change, extra_dependencies) = Self::read_new(r, updatables)?; - replacement in libpijul/src/change/text_changes.rs at line 182
Ok(Self::read_(r, updatables)?.0)Ok(Self::read_new(r, updatables)?.0) - replacement in libpijul/src/change/text_changes.rs at line 185
fn read_<R: BufRead>(fn read_new<R: BufRead>( - replacement in libpijul/src/change/text_changes.rs at line 189
use self::text_changes::*;let mut section = Section::Header(String::new());// read the data// TODO: make this streaminglet mut s = String::new();r.read_to_string(&mut s)?;let i = &s;// parse headerlet (i, m_header) = parse_header(i).map_err(|e| e.to_owned())?;let header = m_header?;dbg!(&header);// parse dependencieslet (i, deps) = parse_dependencies(i).map_err(|e| e.to_owned())?;dbg!(&deps);// parse hunkslet (_, hunks) = parse_hunks(i).map_err(|e| e.to_owned())?;dbg!(&hunks);Change::update(header, deps, hunks, updatables)}fn update(header: ChangeHeader,dependencies: Vec<PrintableDep>,hunks: Vec<(u64, PrintableHunk)>,updatables: &mut HashMap<usize, crate::InodeUpdate>,) -> Result<(Self, HashSet<Hash>), TextDeError> {// TODO: get rid of this whole default change if possible - replacement in libpijul/src/change/text_changes.rs at line 222
header: ChangeHeader {authors: Vec::new(),message: String::new(),description: None,timestamp: chrono::Utc::now(),},header, - edit in libpijul/src/change/text_changes.rs at line 231
};let conclude_section = |change: &mut Change,section: Section,contents: &mut Vec<u8>|-> Result<(), TextDeError> {match section {Section::Header(ref s) => {debug!("header = {:?}", s);change.header = toml::de::from_str(&s)?;Ok(())}Section::Deps => Ok(()),Section::Changes {mut changes,current,..} => {if has_newvertices(¤t) {contents.push(0)}if let Some(c) = current {debug!("next action = {:?}", c);changes.push(c)}change.changes = changes;Ok(())}} - replacement in libpijul/src/change/text_changes.rs at line 232
let mut h = String::new();let mut contents = Vec::new();// process dependencies - replacement in libpijul/src/change/text_changes.rs at line 236[7.89372]→[7.45730:45994](∅→∅),[7.45730]→[7.45730:45994](∅→∅),[7.45994]→[7.8546:8592](∅→∅),[7.8592]→[7.46042:46248](∅→∅),[7.46042]→[7.46042:46248](∅→∅),[7.46248]→[7.89373:89426](∅→∅),[7.89426]→[7.46297:46674](∅→∅),[7.46297]→[7.46297:46674](∅→∅)
while r.read_line(&mut h)? > 0 {debug!("h = {:?}", h);if h == Self::DEPS_LINE {let section = std::mem::replace(&mut section, Section::Deps);conclude_section(&mut change, section, &mut contents)?;} else if h == Self::HUNKS_LINE {let section = std::mem::replace(&mut section,Section::Changes {changes: Vec::new(),current: None,offsets: HashMap::default(),},);conclude_section(&mut change, section, &mut contents)?;} else {use regex::Regex;lazy_static! {static ref DEPS: Regex = Regex::new(r#"\[(\d*|\*)\](\+| ) *(\S*)"#).unwrap();static ref KNOWN: Regex = Regex::new(r#"(\S*)"#).unwrap();for dep in dependencies {let hash = Hash::from_base32(dep.hash.as_bytes()).unwrap();match dep.type_ {DepType::Numbered(n, false) => {change.hashed.dependencies.push(hash);deps.insert(n, hash); - replacement in libpijul/src/change/text_changes.rs at line 243[7.46692]→[7.46692:47781](∅→∅),[7.47781]→[7.8593:8689](∅→∅),[7.8689]→[7.47879:48064](∅→∅),[7.47879]→[7.47879:48064](∅→∅)
match section {Section::Header(ref mut s) => s.push_str(&h),Section::Deps => {if let Some(d) = DEPS.captures(&h) {let hash = Hash::from_base32(d[3].as_bytes()).unwrap();if let Ok(n) = d[1].parse() {if &d[2] == " " {change.hashed.dependencies.push(hash);}deps.insert(n, hash);} else if &d[1] == "*" {change.hashed.extra_known.push(hash);} else {extra_dependencies.insert(hash);}}}Section::Changes {ref mut current,ref mut changes,ref mut offsets,} => {if let Some(next) =Hunk::read(updatables, current, &mut contents, &deps, offsets, &h)?{debug!("next action = {:?}", next);changes.push(next)}}DepType::Numbered(n, true) => {deps.insert(n, hash);}DepType::ExtraKnown => {change.hashed.extra_known.push(hash);}DepType::ExtraUnknown => {extra_dependencies.insert(hash); - replacement in libpijul/src/change/text_changes.rs at line 253
h.clear();}// process hunkslet mut contents = Vec::new();let mut offsets = HashMap::default();for (n, hunk) in hunks {let res =Hunk::from_printable(updatables, &mut contents, &deps, &mut offsets, (n, hunk))?;change.hashed.changes.push(res); - edit in libpijul/src/change/text_changes.rs at line 263
conclude_section(&mut change, section, &mut contents)?; - replacement in libpijul/src/change/text_changes.rs at line 273
const BINARY_LABEL: &str = "binary";pub fn to_printable_new_vertex(atom: &Atom<Option<Hash>>,hashes: &HashMap<Hash, usize>,) -> PrintableNewVertex {if let PrintableAtom::NewVertex(v) = to_printable_atom(atom, hashes) {v} else {panic!("PrintableAtom::NewVertex expected here")}} - replacement in libpijul/src/change/text_changes.rs at line 284
struct Escaped<'a>(&'a str);pub fn to_printable_edge_map(atom: &Atom<Option<Hash>>,hashes: &HashMap<Hash, usize>,) -> Vec<PrintableEdge> {if let PrintableAtom::Edges(v) = to_printable_atom(atom, hashes) {v} else {panic!("PrintableAtom::Edges expected here")}} - replacement in libpijul/src/change/text_changes.rs at line 295
impl<'a> std::fmt::Display for Escaped<'a> {fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {write!(fmt, "\"")?;for c in self.0.chars() {if c == '"' {write!(fmt, "\\{}", c)?} else if c == '\\' {write!(fmt, "\\\\")?} else {write!(fmt, "{}", c)?}}write!(fmt, "\"")?;Ok(())/// Panics if the Atom is not an EdgeMapfn to_printable_atom(atom: &Atom<Option<Hash>>, hashes: &HashMap<Hash, usize>) -> PrintableAtom {match atom {Atom::NewVertex(ref new_vertex) => PrintableAtom::NewVertex(PrintableNewVertex {up_context: new_vertex.up_context.iter().map(|c| to_printable_pos(hashes, *c)).collect(),start: new_vertex.start.0 .0,end: new_vertex.end.0 .0,down_context: new_vertex.down_context.iter().map(|c| to_printable_pos(hashes, *c)).collect(),}),Atom::EdgeMap(ref edge_map) => PrintableAtom::Edges(edge_map.edges.iter().map(|c| PrintableEdge {previous: PrintableEdgeFlags::from(c.previous),flag: PrintableEdgeFlags::from(c.flag),from: to_printable_pos(hashes, c.from),to_start: to_printable_pos(hashes, c.to.start_pos()),to_end: c.to.end.0 .0,introduced_by: *hashes.get(&c.introduced_by.unwrap()).unwrap_or_else(|| {panic!("introduced_by = {:?}, not found", c.introduced_by)}),}).collect(),), - replacement in libpijul/src/change/text_changes.rs at line 331
fn unescape(s: &str) -> std::borrow::Cow<str> {let mut b = 0;let mut result = String::new();let mut ch = s.chars();while let Some(c) = ch.next() {if c == '\\' {if result.is_empty() {result = s.split_at(b).0.to_string();}if let Some(c) = ch.next() {result.push(c)}} else if !result.is_empty() {result.push(c)}b += c.len_utf8()}if result.is_empty() {s.into()} else {result.into()fn from_printable_edge_map(edges: &[PrintableEdge],changes: &HashMap<usize, Hash>,) -> Result<Vec<NewEdge<Option<Hash>>>, TextDeError> {let mut res = Vec::new();for edge in edges {let Position { change, pos } = from_printable_pos(changes, edge.to_start)?;res.push(NewEdge {previous: edge.previous.to(),flag: edge.flag.to(),from: from_printable_pos(changes, edge.from)?,to: Vertex {change,start: pos,end: ChangePosition(L64(edge.to_end)),},introduced_by: change_ref(changes, edge.introduced_by)?,}) - edit in libpijul/src/change/text_changes.rs at line 350
Ok(res) - replacement in libpijul/src/change/text_changes.rs at line 359
mut w: &mut W,w: &mut W, - edit in libpijul/src/change/text_changes.rs at line 361
// let file_name = |local: &Local, _| -> String { format!("{}:{}", local.path, local.line) }; - replacement in libpijul/src/change/text_changes.rs at line 367
metadata: perms,metadata, - replacement in libpijul/src/change/text_changes.rs at line 370[2.100]→[7.1482:1537](∅→∅),[7.2860]→[7.1482:1537](∅→∅),[7.49359]→[7.1482:1537](∅→∅),[7.1537]→[7.1288:1411](∅→∅),[7.1411]→[7.1644:1944](∅→∅),[7.1644]→[7.1644:1944](∅→∅),[7.1944]→[7.49438:49991](∅→∅),[7.49438]→[7.49438:49991](∅→∅)
write!(w,"Moved: {} {} {}",Escaped(&path),Escaped(&name),if perms.0 & 0o1000 == 0o1000 {"+dx "} else if perms.0 & 0o100 == 0o100 {"+x "} else {""})?;write_pos(&mut w, hashes, del.inode())?;writeln!(w)?;write_atom(&mut w, hashes, &del)?;write!(w, "up")?;for c in add.up_context.iter() {write!(w, " ")?;write_pos(&mut w, hashes, *c)?}write!(w, ", down")?;for c in add.down_context.iter() {write!(w, " ")?;write_pos(&mut w, hashes, *c)?PrintableHunk::FileMoveV {path: path.to_string(),name: name.to_string(),perms: PrintablePerms::from_metadata(metadata),pos: to_printable_pos(hashes, del.inode()),up_context: to_printable_pos_vec(hashes, &add.up_context),down_context: to_printable_pos_vec(hashes, &add.down_context),del: to_printable_edge_map(del, hashes), - edit in libpijul/src/change/text_changes.rs at line 379
w.write_all(b"\n")?; - replacement in libpijul/src/change/text_changes.rs at line 380
Atom::EdgeMap(_) => {write!(w, "Moved: {:?} ", path)?;write_pos(&mut w, hashes, del.inode())?;writeln!(w)?;write_atom(&mut w, hashes, &add)?;write_atom(&mut w, hashes, &del)?;}Atom::EdgeMap(_) => PrintableHunk::FileMoveE {path: path.to_string(),pos: to_printable_pos(hashes, del.inode()),add: to_printable_edge_map(add, hashes),del: to_printable_edge_map(del, hashes),}, - replacement in libpijul/src/change/text_changes.rs at line 394[7.269]→[7.1412:1477](∅→∅),[7.1477]→[7.50578:50635](∅→∅),[7.5011]→[7.50578:50635](∅→∅),[7.105716]→[7.50578:50635](∅→∅),[7.50578]→[7.50578:50635](∅→∅),[7.50635]→[7.45:110](∅→∅)
write!(w, "File deletion: {} ", Escaped(path))?;write_pos(&mut w, hashes, del.inode())?;writeln!(w, " {:?}", encoding_label(encoding))?;let (contents_data, content_edges) = if let Some(ref c) = contents {(get_change_contents(changes, c, change_contents)?,to_printable_edge_map(c, hashes),)} else {(Vec::new(), Vec::new())}; - replacement in libpijul/src/change/text_changes.rs at line 403[7.111]→[7.50665:50865](∅→∅),[7.50665]→[7.50665:50865](∅→∅),[7.50865]→[7.5012:5105](∅→∅),[7.2549]→[7.50953:51012](∅→∅),[7.2797]→[7.50953:51012](∅→∅),[7.5105]→[7.50953:51012](∅→∅),[7.50953]→[7.50953:51012](∅→∅)
write_atom(&mut w, hashes, &del)?;if let Some(ref contents) = contents {write_atom(&mut w, hashes, &contents)?;writeln!(w)?;print_change_contents(w, changes, contents, change_contents, encoding)?;} else {writeln!(w)?;PrintableHunk::FileDel {path: path.to_string(),pos: to_printable_pos(hashes, del.inode()),encoding: encoding.clone(),del_edges: to_printable_edge_map(del, hashes),content_edges: content_edges,contents: contents_data, - replacement in libpijul/src/change/text_changes.rs at line 419[7.308]→[7.1478:1546](∅→∅),[7.1546]→[7.51227:51286](∅→∅),[7.5322]→[7.51227:51286](∅→∅),[7.105779]→[7.51227:51286](∅→∅),[7.51227]→[7.51227:51286](∅→∅),[7.51286]→[7.160:225](∅→∅),[7.225]→[7.51316:51484](∅→∅),[7.51316]→[7.51316:51484](∅→∅),[7.51484]→[7.5323:5416](∅→∅)
write!(w, "File un-deletion: {} ", Escaped(path))?;write_pos(&mut w, hashes, undel.inode())?;writeln!(w, " {:?}", encoding_label(encoding))?;write_atom(&mut w, hashes, &undel)?;if let Some(ref contents) = contents {write_atom(&mut w, hashes, &contents)?;print_change_contents(w, changes, contents, change_contents, encoding)?;let (contents_data, content_edges) = if let Some(ref c) = contents {(get_change_contents(changes, c, change_contents)?,to_printable_edge_map(c, hashes),) - replacement in libpijul/src/change/text_changes.rs at line 425
writeln!(w)?;(Vec::new(), Vec::new())};PrintableHunk::FileUndel {path: path.to_string(),pos: to_printable_pos(hashes, undel.inode()),encoding: encoding.clone(),undel_edges: to_printable_edge_map(undel, hashes),content_edges: content_edges,contents: contents_data, - replacement in libpijul/src/change/text_changes.rs at line 448
metadata: perms,metadata, - replacement in libpijul/src/change/text_changes.rs at line 451
let parent = if let Some(p) = crate::path::parent(&path) {if p.is_empty() {"/"} else {p}let contents = if let Some(Atom::NewVertex(ref n)) = contents {change_contents[n.start.us()..n.end.us()].to_vec() - replacement in libpijul/src/change/text_changes.rs at line 455
"/"Vec::new() - edit in libpijul/src/change/text_changes.rs at line 457[7.52563]→[7.52563:52618](∅→∅),[7.52618]→[7.1547:1693](∅→∅),[7.1693]→[7.2070:2320](∅→∅),[7.2070]→[7.2070:2320](∅→∅),[7.2320]→[7.166:193](∅→∅),[7.193]→[7.2641:2690](∅→∅),[7.232]→[7.52730:52754](∅→∅),[7.2346]→[7.52730:52754](∅→∅),[7.2690]→[7.52730:52754](∅→∅),[7.3523]→[7.52730:52754](∅→∅),[7.52730]→[7.52730:52754](∅→∅)
write!(w,"File addition: {} in {}{} \"{}\"\n up",Escaped(name),Escaped(parent),if perms.0 & 0o1000 == 0o1000 {" +dx"} else if perms.0 & 0o100 == 0o100 {" +x"} else {""},encoding_label(encoding))?; - replacement in libpijul/src/change/text_changes.rs at line 458
for c in n.up_context.iter() {write!(w, " ")?;write_pos(&mut w, hashes, *c)?PrintableHunk::FileAddition {name: name.to_string(),parent: crate::path::parent(&path).unwrap_or("").to_string(),perms: PrintablePerms::from_metadata(metadata),encoding: encoding.clone(),up_context: to_printable_pos_vec(hashes, &n.up_context),start: n.start.0 .0,end: n.end.0 .0,contents, - replacement in libpijul/src/change/text_changes.rs at line 469[7.52979]→[7.52979:53131](∅→∅),[7.53131]→[7.140788:140860](∅→∅),[7.140860]→[7.1177:1235](∅→∅),[7.448]→[7.53268:53313](∅→∅),[7.1235]→[7.53268:53313](∅→∅),[7.2930]→[7.53268:53313](∅→∅),[7.53268]→[7.53268:53313](∅→∅),[7.53313]→[7.123:168](∅→∅),[7.168]→[7.53356:53378](∅→∅),[7.53356]→[7.53356:53378](∅→∅)
writeln!(w, ", new {}:{}", n.start.0, n.end.0)?;}if let Some(Atom::NewVertex(ref n)) = contents {let c = &change_contents[n.start.us()..n.end.us()];print_contents(w, "+", c, encoding)?;if !c.ends_with(b"\n") {writeln!(w, "\n\\")?}} else {// TODO: should this panic?panic!("Invalid Hunk::FileAdd field add_name: {:?}", add_name); - replacement in libpijul/src/change/text_changes.rs at line 480[7.341]→[5.106:186](∅→∅),[5.186]→[7.53536:53596](∅→∅),[7.1778]→[7.53536:53596](∅→∅),[7.53536]→[7.53536:53596](∅→∅),[7.53596]→[7.2809:2872](∅→∅),[7.2872]→[7.53596:53680](∅→∅),[7.53596]→[7.53596:53680](∅→∅),[7.53680]→[7.2873:2960](∅→∅)
write!(w, "Edit in {}:{} ", Escaped(&local.path), local.line)?;write_pos(&mut w, hashes, change.inode())?;write!(w, " {:?}", encoding_label(encoding))?;writeln!(w)?;write_atom(&mut w, hashes, &change)?;print_change_contents(w, changes, change, change_contents, encoding)?;PrintableHunk::Edit {path: local.path.clone(),line: local.line,pos: to_printable_pos(hashes, change.inode()),encoding: encoding.clone(),change: to_printable_atom(change, hashes),contents: get_change_contents(changes, change, change_contents)?,} - replacement in libpijul/src/change/text_changes.rs at line 496[7.381]→[5.187:274](∅→∅),[5.274]→[7.53990:54050](∅→∅),[7.1870]→[7.53990:54050](∅→∅),[7.53990]→[7.53990:54050](∅→∅),[7.54050]→[7.2988:3051](∅→∅),[7.3051]→[7.54050:54193](∅→∅),[7.54050]→[7.54050:54193](∅→∅),[7.54193]→[7.3052:3231](∅→∅),[7.3168]→[7.54362:54376](∅→∅),[7.3231]→[7.54362:54376](∅→∅),[7.54362]→[7.54362:54376](∅→∅),[7.54376]→[7.8955:9011](∅→∅),[7.9011]→[7.1871:1948](∅→∅),[7.1948]→[7.54504:54753](∅→∅),[7.54504]→[7.54504:54753](∅→∅),[7.54753]→[7.9012:9070](∅→∅),[7.9070]→[7.1949:2029](∅→∅),[7.2029]→[7.54886:55121](∅→∅),[7.54886]→[7.54886:55121](∅→∅)
write!(w, "Replacement in {}:{} ", Escaped(&local.path), local.line)?;write_pos(&mut w, hashes, change.inode())?;write!(w, " {:?}", encoding_label(encoding))?;writeln!(w)?;write_atom(&mut w, hashes, &change)?;write_atom(&mut w, hashes, &replacement)?;print_change_contents(w, changes, change, change_contents, encoding)?;print_change_contents(w, changes, replacement, change_contents, encoding)?;}Hunk::SolveNameConflict { name, path } => {write!(w, "Solving a name conflict in {} ", Escaped(path))?;write_pos(&mut w, hashes, name.inode())?;write!(w, ": ")?;write_deleted_names(&mut w, changes, name)?;writeln!(w)?;write_atom(&mut w, hashes, &name)?;}Hunk::UnsolveNameConflict { name, path } => {write!(w, "Un-solving a name conflict in {} ", Escaped(path))?;write_pos(&mut w, hashes, name.inode())?;write!(w, ": ")?;write_deleted_names(&mut w, changes, name)?;writeln!(w)?;write_atom(&mut w, hashes, &name)?;PrintableHunk::Replace {path: local.path.clone(),line: local.line,pos: to_printable_pos(hashes, change.inode()),encoding: encoding.clone(),change: to_printable_edge_map(change, hashes),replacement: to_printable_new_vertex(replacement, hashes),change_contents: get_change_contents(changes, change, change_contents)?,replacement_contents: get_change_contents(changes,replacement,change_contents,)?,} - edit in libpijul/src/change/text_changes.rs at line 511
Hunk::SolveNameConflict { name, path } => PrintableHunk::SolveNameConflict {path: path.clone(),pos: to_printable_pos(hashes, name.inode()),names: get_deleted_names(changes, name)?,edges: to_printable_edge_map(name, hashes),},Hunk::UnsolveNameConflict { name, path } => PrintableHunk::UnsolveNameConflict {path: path.clone(),pos: to_printable_pos(hashes, name.inode()),names: get_deleted_names(changes, name)?,edges: to_printable_edge_map(name, hashes),}, - replacement in libpijul/src/change/text_changes.rs at line 524[7.9131]→[7.382:430](∅→∅),[7.55197]→[7.382:430](∅→∅),[7.430]→[7.55197:55244](∅→∅),[7.55197]→[7.55197:55244](∅→∅),[7.55244]→[5.275:334](∅→∅),[5.334]→[7.2093:2167](∅→∅),[7.2093]→[7.2093:2167](∅→∅),[7.2167]→[7.55354:55518](∅→∅),[7.55354]→[7.55354:55518](∅→∅),[7.55518]→[7.3232:3316](∅→∅)
debug!("solve order conflict");write!(w,"Solving an order conflict in {}:{} ",Escaped(&local.path),local.line,)?;write_pos(&mut w, hashes, change.inode())?;writeln!(w)?;write_atom(&mut w, hashes, &change)?;print_change_contents(w, changes, change, change_contents, &None)?;// TODO: pass in the encodinglet contents = get_change_contents(changes, change, change_contents)?;let encoding = get_encoding(&contents);PrintableHunk::SolveOrderConflict {path: local.path.clone(),line: local.line,pos: to_printable_pos(hashes, change.inode()),encoding: encoding.clone(),change: to_printable_new_vertex(change, hashes),contents: get_change_contents(changes, change, change_contents)?,} - replacement in libpijul/src/change/text_changes.rs at line 537[7.9194]→[7.431:481](∅→∅),[7.55678]→[7.431:481](∅→∅),[7.481]→[7.55678:55725](∅→∅),[7.55678]→[7.55678:55725](∅→∅),[7.55725]→[5.335:397](∅→∅),[5.397]→[7.2234:2308](∅→∅),[7.2234]→[7.2234:2308](∅→∅),[7.2308]→[7.55838:56002](∅→∅),[7.55838]→[7.55838:56002](∅→∅),[7.56002]→[7.3317:3401](∅→∅)
debug!("unsolve order conflict");write!(w,"Un-solving an order conflict in {}:{} ",Escaped(&local.path),local.line,)?;write_pos(&mut w, hashes, change.inode())?;writeln!(w)?;write_atom(&mut w, hashes, &change)?;print_change_contents(w, changes, change, change_contents, &None)?;// TODO: pass in the encodinglet contents = get_change_contents(changes, change, change_contents)?;let encoding = get_encoding(&contents);PrintableHunk::UnsolveOrderConflict {path: local.path.clone(),line: local.line,pos: to_printable_pos(hashes, change.inode()),encoding: encoding.clone(),change: to_printable_edge_map(change, hashes),contents: get_change_contents(changes, change, change_contents)?,} - replacement in libpijul/src/change/text_changes.rs at line 553[7.3512]→[7.3512:3531](∅→∅),[7.3531]→[7.482:527](∅→∅),[7.9253]→[7.482:527](∅→∅),[7.56158]→[7.482:527](∅→∅),[7.527]→[7.56158:56205](∅→∅),[7.56158]→[7.56158:56205](∅→∅),[7.56205]→[7.2309:2368](∅→∅),[7.2368]→[5.398:471](∅→∅),[5.471]→[7.56309:56389](∅→∅),[7.2421]→[7.56309:56389](∅→∅),[7.56309]→[7.56309:56389](∅→∅),[7.56389]→[7.2422:2487](∅→∅),[7.2487]→[7.56389:56473](∅→∅),[7.3595]→[7.56389:56473](∅→∅),[7.56389]→[7.56389:56473](∅→∅),[7.56473]→[7.5445:5532](∅→∅),[7.3402]→[7.56555:56569](∅→∅),[7.3680]→[7.56555:56569](∅→∅),[7.5532]→[7.56555:56569](∅→∅),[7.56555]→[7.56555:56569](∅→∅)
} => {debug!("resurrect zombies");write!(w,"Resurrecting zombie lines in {}:{} ",Escaped(&local.path),local.line)?;write_pos(&mut w, hashes, change.inode())?;write!(w, " \"{}\"", encoding_label(encoding))?;writeln!(w)?;write_atom(&mut w, hashes, &change)?;print_change_contents(w, changes, change, change_contents, encoding)?;}} => PrintableHunk::ResurrectZombies {path: local.path.clone(),line: local.line,pos: to_printable_pos(hashes, change.inode()),encoding: encoding.clone(),change: to_printable_edge_map(change, hashes),contents: get_change_contents(changes, change, change_contents)?,}, - edit in libpijul/src/change/text_changes.rs at line 562
.write(w)?; - edit in libpijul/src/change/text_changes.rs at line 567
fn encoding_label(encoding: &Option<Encoding>) -> &str {match encoding {Some(encoding) => encoding.label(),_ => BINARY_LABEL,}} - replacement in libpijul/src/change/text_changes.rs at line 568
fn read(fn from_printable( - edit in libpijul/src/change/text_changes.rs at line 570
current: &mut Option<Self>, - replacement in libpijul/src/change/text_changes.rs at line 573[7.56877]→[7.56877:57069](∅→∅),[7.57069]→[7.2083:2233](∅→∅),[7.127]→[7.57195:57232](∅→∅),[7.286]→[7.57195:57232](∅→∅),[7.384]→[7.57195:57232](∅→∅),[7.2233]→[7.57195:57232](∅→∅),[7.2476]→[7.57195:57232](∅→∅),[7.3673]→[7.57195:57232](∅→∅),[7.3992]→[7.57195:57232](∅→∅),[7.89589]→[7.57195:57232](∅→∅),[7.57195]→[7.57195:57232](∅→∅),[7.57232]→[7.3993:4103](∅→∅),[7.216]→[7.57316:57360](∅→∅),[7.4103]→[7.57316:57360](∅→∅),[7.57316]→[7.57316:57360](∅→∅),[7.57360]→[7.4104:4221](∅→∅),[7.312]→[7.57451:57497](∅→∅),[7.4221]→[7.57451:57497](∅→∅),[7.57451]→[7.57451:57497](∅→∅),[7.57497]→[7.5533:5646](∅→∅),[7.404]→[7.57584:57632](∅→∅),[7.5646]→[7.57584:57632](∅→∅),[7.57584]→[7.57584:57632](∅→∅),[7.57632]→[7.5647:5763](∅→∅),[7.499]→[7.57722:57759](∅→∅),[7.5763]→[7.57722:57759](∅→∅),[7.57722]→[7.57722:57759](∅→∅),[7.57759]→[7.287:417](∅→∅),[7.417]→[7.627:726](∅→∅),[7.2607]→[7.627:726](∅→∅),[7.627]→[7.627:726](∅→∅),[7.726]→[7.57977:58035](∅→∅),[7.57977]→[7.57977:58035](∅→∅),[7.58035]→[7.727:823](∅→∅),[7.823]→[7.58127:58223](∅→∅),[7.58127]→[7.58127:58223](∅→∅),[7.58223]→[7.824:925](∅→∅),[7.925]→[7.58320:58396](∅→∅),[7.58320]→[7.58320:58396](∅→∅),[7.58396]→[7.4222:4370](∅→∅),[7.1052]→[7.58518:58878](∅→∅),[7.4370]→[7.58518:58878](∅→∅),[7.58518]→[7.58518:58878](∅→∅)
h: &str,) -> Result<Option<Self>, TextDeError> {use self::text_changes::*;use regex::Regex;lazy_static! {static ref FILE_ADDITION: Regex =Regex::new(r#"^(?P<n>\d+)\. File addition: "(?P<name>[^"]*)" in "(?P<parent>[^"]*)"(?P<perm> \S+)? "(?P<encoding>[^"]*)""#).unwrap();static ref EDIT: Regex =Regex::new(r#"^([0-9]+)\. Edit in ([^:]+):(\d+) (\d+\.\d+) "(?P<encoding>[^"]*)""#).unwrap();static ref REPLACEMENT: Regex =Regex::new(r#"^([0-9]+)\. Replacement in ([^:]+):(\d+) (\d+\.\d+) "(?P<encoding>[^"]*)""#).unwrap();static ref FILE_DELETION: Regex =Regex::new(r#"^([0-9]+)\. File deletion: "([^"]*)" (\d+\.\d+) "(?P<encoding>[^"]*)""#).unwrap();static ref FILE_UNDELETION: Regex =Regex::new(r#"^([0-9]+)\. File un-deletion: "([^"]*)" (\d+\.\d+) "(?P<encoding>[^"]*)""#).unwrap();static ref MOVE: Regex =Regex::new(r#"^([0-9]+)\. Moved: "(?P<former>[^"]*)" "(?P<new>[^"]*)" (?P<perm>[^ ]+ )?(?P<inode>.*)"#).unwrap();static ref MOVE_: Regex = Regex::new(r#"^([0-9]+)\. Moved: "([^"]*)" (.*)"#).unwrap();static ref NAME_CONFLICT: Regex = Regex::new(r#"^([0-9]+)\. ((Solving)|(Un-solving)) a name conflict in "([^"]*)" (.*): .*"#).unwrap();static ref ORDER_CONFLICT: Regex = Regex::new(r#"^([0-9]+)\. ((Solving)|(Un-solving)) an order conflict in (.*):(\d+) (\d+\.\d+)"#).unwrap();static ref ZOMBIE: Regex =Regex::new(r#"^([0-9]+)\. Resurrecting zombie lines in (?P<path>"[^"]+"):(?P<line>\d+) (?P<inode>\d+\.\d+) "(?P<encoding>[^"]*)""#).unwrap();static ref CONTEXT: Regex = Regex::new(r#"up ((\d+\.\d+ )*\d+\.\d+)(, new (\d+):(\d+))?(, down ((\d+\.\d+ )*\d+\.\d+))?"#).unwrap();}if let Some(cap) = FILE_ADDITION.captures(h) {if has_newvertices(current) {contents_.push(0)(hunk_id, hunk): (u64, PrintableHunk),) -> Result<Self, TextDeError> {// TODO: should we push, or not?contents_.push(0);match hunk {PrintableHunk::FileMoveV {path,name,perms,pos,up_context,down_context,del,} => {let mut add = default_newvertex();add.start = ChangePosition(contents_.len().into());add.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;let meta = FileMetadata {metadata: InodeMetadata(match perms {// TODO: deduplicatePrintablePerms::IsDir => 0o1100,PrintablePerms::IsExecutable => 0o100,PrintablePerms::IsFile => 0,}),basename: &name,encoding: None,};meta.write(contents_);add.end = ChangePosition(contents_.len().into());add.up_context = from_printable_pos_vec_offsets(changes, offsets, &up_context)?;add.down_context = from_printable_pos_vec_offsets(changes, offsets, &down_context)?;Ok(Hunk::FileMove {add: Atom::NewVertex(add),del: Atom::EdgeMap(EdgeMap {inode: from_printable_pos(changes, pos)?,edges: from_printable_edge_map(&del, changes)?,}),path,})}PrintableHunk::FileMoveE {path,pos,add,del,} => {let inode = from_printable_pos(changes, pos)?;Ok(Hunk::FileMove {add: Atom::EdgeMap(EdgeMap {inode,edges: from_printable_edge_map(&add, changes)?,}),del: Atom::EdgeMap(EdgeMap {inode,edges: from_printable_edge_map(&del, changes)?,}),path,}) - replacement in libpijul/src/change/text_changes.rs at line 634[7.58892]→[7.58892:58944](∅→∅),[7.58944]→[7.140861:140930](∅→∅),[7.140930]→[7.59013:59079](∅→∅),[7.59013]→[7.59013:59079](∅→∅),[7.59079]→[7.2488:2558](∅→∅),[7.2558]→[7.59139:59326](∅→∅),[7.59139]→[7.59139:59326](∅→∅),[7.59326]→[7.2559:2642](∅→∅),[7.2642]→[7.59391:59406](∅→∅),[7.59391]→[7.59391:59406](∅→∅),[7.59406]→[7.89590:89629](∅→∅),[7.89629]→[7.2608:2670](∅→∅),[7.59406]→[7.2608:2670](∅→∅),[7.2670]→[7.418:463](∅→∅),[7.463]→[7.2714:2741](∅→∅),[7.2714]→[7.2714:2741](∅→∅),[7.2741]→[7.464:515](∅→∅),[7.515]→[7.2791:2864](∅→∅),[7.2791]→[7.2791:2864](∅→∅)
let mut add_name = default_newvertex();add_name.start = ChangePosition(contents_.len().into());add_name.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;let name = unescape(&cap.name("name").unwrap().as_str());let path = {let parent = cap.name("parent").unwrap().as_str();(if parent == "/" {String::new()} else {unescape(&parent).to_string() + "/"}) + &name};debug!("cap = {:?}", cap);let meta = if let Some(perm) = cap.name("perm") {if perm.as_str() == " +dx" {0o1100} else if perm.as_str() == " +x" {0o100} else {0PrintableHunk::FileAddition {name,parent,perms,encoding,up_context,start,end,contents,} => {let meta = FileMetadata {metadata: InodeMetadata(match perms {PrintablePerms::IsDir => 0o1100,PrintablePerms::IsExecutable => 0o100,PrintablePerms::IsFile => 0,}),basename: &name,encoding: encoding.clone(),};let add_name = {let mut x = default_newvertex();x.start = ChangePosition(contents_.len().into());meta.write(contents_);x.end = ChangePosition(contents_.len().into());x.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;x.up_context = from_printable_pos_vec_offsets(changes, offsets, &up_context)?;x};let add_inode = {let mut x = default_newvertex();x.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;x.up_context.push(Position {change: None,pos: ChangePosition(contents_.len().into()),});contents_.push(0);x.start = ChangePosition(contents_.len().into());x.end = ChangePosition(contents_.len().into());contents_.push(0);x};if let Entry::Occupied(mut e) = updatables.entry(hunk_id as usize) {if let crate::InodeUpdate::Add { ref mut pos, .. } = e.get_mut() {offsets.insert(pos.0.into(), add_inode.start);*pos = add_inode.start} - edit in libpijul/src/change/text_changes.rs at line 685[7.2882]→[7.2882:2936](∅→∅),[7.2936]→[7.5764:5887](∅→∅),[7.5887]→[7.3044:3129](∅→∅),[7.2936]→[7.3044:3129](∅→∅),[7.3129]→[7.2643:2676](∅→∅),[7.2676]→[7.5888:5932](∅→∅),[7.3161]→[7.5888:5932](∅→∅),[7.5932]→[7.3161:3176](∅→∅),[7.3161]→[7.3161:3176](∅→∅),[7.3176]→[7.2234:2269](∅→∅),[7.2269]→[7.140931:140998](∅→∅),[7.3216]→[7.140931:140998](∅→∅),[7.59747]→[7.140931:140998](∅→∅)
} else {0};let n = cap.name("n").unwrap().as_str().parse().unwrap();let encoding = encoding_from_label(cap);let meta = FileMetadata {metadata: InodeMetadata(meta),basename: &name,encoding: encoding.clone(),};meta.write(contents_);add_name.end = ChangePosition(contents_.len().into()); - replacement in libpijul/src/change/text_changes.rs at line 686[7.59815]→[7.59815:60014](∅→∅),[7.60014]→[7.140999:141060](∅→∅),[7.141060]→[7.60075:60091](∅→∅),[7.60075]→[7.60075:60091](∅→∅)
let mut add_inode = default_newvertex();add_inode.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;add_inode.up_context.push(Position {change: None,pos: ChangePosition(contents_.len().into()),});// contextoffsets.insert(start, add_name.start);offsets.insert(end, add_name.end);offsets.insert(end + 1, add_name.end + 1); - replacement in libpijul/src/change/text_changes.rs at line 691[7.60092]→[7.60092:60123](∅→∅),[7.60123]→[7.141061:141199](∅→∅),[7.141199]→[7.60261:60292](∅→∅),[7.60261]→[7.60261:60292](∅→∅),[7.60362]→[7.60362:60511](∅→∅),[7.60511]→[3.0:67](∅→∅)
contents_.push(0);add_inode.start = ChangePosition(contents_.len().into());add_inode.end = ChangePosition(contents_.len().into());contents_.push(0);if let Entry::Occupied(mut e) = updatables.entry(n) {if let crate::InodeUpdate::Add { ref mut pos, .. } = e.get_mut() {offsets.insert(pos.0.into(), add_inode.start);// contentslet contents_res = {let mut x = default_newvertex();// The `-1` here comes from the extra 0// padding bytes pushed onto `contents_`.// TODO: verify this is correctlet inode = Position {change: None,pos: ChangePosition((contents_.len() - 1).into()),};x.up_context.push(inode);x.inode = inode;x.flag = EdgeFlags::BLOCK;x.start = ChangePosition(contents_.len().into());contents_.extend(&contents);x.end = ChangePosition(contents_.len().into());x}; - replacement in libpijul/src/change/text_changes.rs at line 710[3.68]→[7.60511:60586](∅→∅),[7.60511]→[7.60511:60586](∅→∅),[7.3925]→[7.60586:60645](∅→∅),[7.60586]→[7.60586:60645](∅→∅),[7.60645]→[7.9288:9325](∅→∅)
*pos = add_inode.start}}Ok(std::mem::replace(current,Some(Hunk::FileAdd {Ok(Hunk::FileAdd { - replacement in libpijul/src/change/text_changes.rs at line 713
contents: None,path,contents: Some(Atom::NewVertex(contents_res)),path: parent + "/" + &name, - replacement in libpijul/src/change/text_changes.rs at line 716[7.3956]→[7.60862:61026](∅→∅),[7.4427]→[7.60862:61026](∅→∅),[7.5963]→[7.60862:61026](∅→∅),[7.60862]→[7.60862:61026](∅→∅)
}),))} else if let Some(cap) = EDIT.captures(h) {if has_newvertices(current) {contents_.push(0)}) - replacement in libpijul/src/change/text_changes.rs at line 718[7.61040]→[7.61040:61086](∅→∅),[7.61086]→[7.128:180](∅→∅),[7.180]→[7.69:108](∅→∅),[7.108]→[7.61137:61196](∅→∅),[7.180]→[7.61137:61196](∅→∅),[7.61137]→[7.61137:61196](∅→∅),[7.61196]→[7.9326:9360](∅→∅),[7.9360]→[7.61232:61443](∅→∅),[7.61232]→[7.61232:61443](∅→∅),[7.61443]→[7.4428:4484](∅→∅)
let mut v = default_newvertex();v.inode = parse_pos(changes, &cap[4])?;v.flag = EdgeFlags::BLOCK;Ok(std::mem::replace(current,Some(Hunk::Edit {change: Atom::NewVertex(v),local: Local {path: cap[2].to_string(),line: cap[3].parse().unwrap(),},encoding: encoding_from_label(cap),PrintableHunk::FileDel {path,pos,encoding,del_edges,content_edges,contents: _,} => Ok(Hunk::FileDel {del: Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&del_edges, changes)?,inode: from_printable_pos(changes, pos)?, - replacement in libpijul/src/change/text_changes.rs at line 730[7.61463]→[7.61463:61673](∅→∅),[7.61673]→[7.181:233](∅→∅),[7.233]→[7.109:148](∅→∅),[7.148]→[7.61724:61783](∅→∅),[7.233]→[7.61724:61783](∅→∅),[7.61724]→[7.61724:61783](∅→∅),[7.61783]→[7.9361:9402](∅→∅),[7.9402]→[7.61826:62098](∅→∅),[7.61826]→[7.61826:62098](∅→∅),[7.62098]→[7.4485:4541](∅→∅),[7.4541]→[7.62098:62330](∅→∅),[7.62098]→[7.62098:62330](∅→∅),[7.62330]→[7.234:288](∅→∅),[7.288]→[7.62383:62442](∅→∅),[7.62383]→[7.62383:62442](∅→∅),[7.62442]→[7.9403:9440](∅→∅),[7.9440]→[7.62481:62608](∅→∅),[7.62481]→[7.62481:62608](∅→∅),[7.62608]→[7.5964:6020](∅→∅),[7.6020]→[7.62608:62844](∅→∅),[7.62608]→[7.62608:62844](∅→∅),[7.62844]→[7.289:345](∅→∅),[7.345]→[7.62899:62958](∅→∅),[7.62899]→[7.62899:62958](∅→∅),[7.62958]→[7.9441:9480](∅→∅),[7.9480]→[7.62999:63130](∅→∅),[7.62999]→[7.62999:63130](∅→∅),[7.63130]→[7.6021:6077](∅→∅)
))} else if let Some(cap) = REPLACEMENT.captures(h) {if has_newvertices(current) {contents_.push(0)}let mut v = default_newvertex();v.inode = parse_pos(changes, &cap[4])?;v.flag = EdgeFlags::BLOCK;Ok(std::mem::replace(current,Some(Hunk::Replacement {change: Atom::NewVertex(v.clone()),replacement: Atom::NewVertex(v),local: Local {path: cap[2].to_string(),line: cap[3].parse().unwrap(),},encoding: encoding_from_label(cap),}),))} else if let Some(cap) = FILE_DELETION.captures(h) {if has_newvertices(current) {contents_.push(0)}let mut del = default_edgemap();del.inode = parse_pos(changes, &cap[3])?;Ok(std::mem::replace(current,Some(Hunk::FileDel {del: Atom::EdgeMap(del),contents: None,path: cap[2].to_string(),encoding: encoding_from_label(cap),}),))} else if let Some(cap) = FILE_UNDELETION.captures(h) {if has_newvertices(current) {contents_.push(0)}let mut undel = default_edgemap();undel.inode = parse_pos(changes, &cap[3])?;Ok(std::mem::replace(current,Some(Hunk::FileUndel {undel: Atom::EdgeMap(undel),contents: None,path: cap[2].to_string(),encoding: encoding_from_label(cap),contents: Some(Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&content_edges, changes)?,inode: from_printable_pos(changes, pos)?,})),path,encoding,}),PrintableHunk::FileUndel {path,pos,encoding,undel_edges,content_edges,contents: _,} => Ok(Hunk::FileUndel {undel: Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&undel_edges, changes)?,inode: from_printable_pos(changes, pos)?, - replacement in libpijul/src/change/text_changes.rs at line 749[7.63150]→[7.63150:63402](∅→∅),[7.63402]→[7.346:401](∅→∅),[7.401]→[7.63456:63557](∅→∅),[7.63456]→[7.63456:63557](∅→∅),[7.63557]→[7.9481:9532](∅→∅),[7.9532]→[7.63610:63759](∅→∅),[7.63610]→[7.63610:63759](∅→∅),[7.63759]→[7.9533:9586](∅→∅),[7.9586]→[7.63814:64101](∅→∅),[7.63814]→[7.63814:64101](∅→∅)
))} else if let Some(cap) = NAME_CONFLICT.captures(h) {if has_newvertices(current) {contents_.push(0)}let mut name = default_edgemap();debug!("cap = {:?}", cap);name.inode = parse_pos(changes, &cap[6])?;Ok(std::mem::replace(current,if &cap[2] == "Solving" {Some(Hunk::SolveNameConflict {name: Atom::EdgeMap(name),path: cap[5].to_string(),})} else {Some(Hunk::UnsolveNameConflict {name: Atom::EdgeMap(name),path: cap[5].to_string(),})},))} else if let Some(cap) = MOVE.captures(h) {if has_newvertices(current) {contents_.push(0)contents: Some(Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&content_edges, changes)?,inode: from_printable_pos(changes, pos)?,})),path,encoding,}),PrintableHunk::Edit {path,line,pos,encoding,change,contents,} => {let inode = from_printable_pos(changes, pos)?;let change = match change {PrintableAtom::NewVertex(new_vertex) => {let mut x = default_newvertex();x.inode = inode;x.flag = EdgeFlags::BLOCK;x.up_context = from_printable_pos_vec(changes, &new_vertex.up_context)?;x.down_context = from_printable_pos_vec(changes, &new_vertex.down_context)?;x.start = ChangePosition(contents_.len().into());contents_.extend(&contents);x.end = ChangePosition(contents_.len().into());Atom::NewVertex(x)}PrintableAtom::Edges(edges) => Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&edges, changes)?,inode: inode,}),};Ok(Hunk::Edit {change,local: Local { path, line },encoding,}) - replacement in libpijul/src/change/text_changes.rs at line 789[7.64115]→[7.64115:64162](∅→∅),[7.64162]→[7.141200:141264](∅→∅),[7.141264]→[7.64226:64287](∅→∅),[7.64226]→[7.64226:64287](∅→∅),[7.64287]→[7.2677:2745](∅→∅),[7.2745]→[7.2937:2999](∅→∅),[7.64345]→[7.2937:2999](∅→∅),[7.2999]→[7.516:615](∅→∅),[7.615]→[7.3043:3070](∅→∅),[7.3043]→[7.3043:3070](∅→∅),[7.3070]→[7.616:667](∅→∅),[7.667]→[7.3120:3265](∅→∅),[7.3120]→[7.3120:3265](∅→∅),[7.3265]→[7.3217:3302](∅→∅),[7.3302]→[7.2746:2779](∅→∅),[7.2779]→[7.6078:6110](∅→∅),[7.3334]→[7.6078:6110](∅→∅),[7.6110]→[7.3334:3349](∅→∅),[7.3334]→[7.3334:3349](∅→∅),[7.3349]→[7.2270:2305](∅→∅),[7.2305]→[7.141265:141327](∅→∅),[7.3389]→[7.141265:141327](∅→∅),[7.64686]→[7.141265:141327](∅→∅)
let mut add = default_newvertex();add.start = ChangePosition(contents_.len().into());add.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;let name = unescape(cap.name("new").unwrap().as_str());let meta = if let Some(perm) = cap.name("perm") {debug!("perm = {:?}", perm.as_str());if perm.as_str() == "+dx " {0o1100} else if perm.as_str() == "+x " {0o100} else {0}} else {0};let meta = FileMetadata {metadata: InodeMetadata(meta),basename: &name,encoding: None,};meta.write(contents_);add.end = ChangePosition(contents_.len().into());PrintableHunk::Replace {path,line,pos,encoding,change,replacement,change_contents: _,replacement_contents,} => {let inode = from_printable_pos(changes, pos)?; - replacement in libpijul/src/change/text_changes.rs at line 801[7.64749]→[7.64749:64794](∅→∅),[7.64794]→[7.402:484](∅→∅),[7.484]→[7.64875:64934](∅→∅),[7.64875]→[7.64875:64934](∅→∅),[7.64934]→[7.9587:9625](∅→∅),[7.9625]→[7.64974:65277](∅→∅),[7.64974]→[7.64974:65277](∅→∅)
let mut del = default_edgemap();del.inode = parse_pos(changes, cap.name("inode").unwrap().as_str())?;Ok(std::mem::replace(current,Some(Hunk::FileMove {del: Atom::EdgeMap(del),add: Atom::NewVertex(add),path: cap[2].to_string(),}),))} else if let Some(cap) = MOVE_.captures(h) {if has_newvertices(current) {contents_.push(0)let replacement = {let mut x = default_newvertex();x.inode = inode;x.flag = EdgeFlags::BLOCK;x.up_context = from_printable_pos_vec(changes, &replacement.up_context)?;x.down_context = from_printable_pos_vec(changes, &replacement.down_context)?;x.start = ChangePosition(contents_.len().into());contents_.extend(&replacement_contents);x.end = ChangePosition(contents_.len().into());Atom::NewVertex(x)};Ok(Hunk::Replacement {change: Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&change, changes)?,inode: inode,}),replacement,local: Local { path, line },encoding,}) - replacement in libpijul/src/change/text_changes.rs at line 823[7.65291]→[7.65291:65381](∅→∅),[7.65381]→[7.485:539](∅→∅),[7.539]→[7.65434:65528](∅→∅),[7.65434]→[7.65434:65528](∅→∅),[7.65528]→[7.9626:9664](∅→∅),[7.9664]→[7.65568:65704](∅→∅),[7.65568]→[7.65568:65704](∅→∅)
let mut add = default_edgemap();let mut del = default_edgemap();add.inode = parse_pos(changes, &cap[3])?;del.inode = add.inode;Ok(std::mem::replace(current,Some(Hunk::FileMove {del: Atom::EdgeMap(del),add: Atom::EdgeMap(add),path: cap[2].to_string(),PrintableHunk::SolveNameConflict {path,pos,names: _,edges,} => Ok(Hunk::SolveNameConflict {name: Atom::EdgeMap(EdgeMap {inode: from_printable_pos(changes, pos)?,edges: from_printable_edge_map(&edges, changes)?, - replacement in libpijul/src/change/text_changes.rs at line 833
))} else if let Some(cap) = ORDER_CONFLICT.captures(h) {if has_newvertices(current) {contents_.push(0)}path,}),PrintableHunk::UnsolveNameConflict {path,pos,names: _,edges,} => Ok(Hunk::UnsolveNameConflict {name: Atom::EdgeMap(EdgeMap {inode: from_printable_pos(changes, pos)?,edges: from_printable_edge_map(&edges, changes)?,}),path,}),PrintableHunk::SolveOrderConflict {path,line,pos,encoding: _,change,contents,} => {// TODO: this code block looks suspect. Check the correctness.let mut c = default_newvertex();c.inode = from_printable_pos(changes, pos)?;c.up_context = from_printable_pos_vec(changes, &change.up_context)?;c.down_context = from_printable_pos_vec(changes, &change.down_context)?;// TODO: this maths is probably unnecessarily complicatedc.start = ChangePosition(contents_.len().into());c.end = ChangePosition((contents_.len() as u64 + change.end - change.start).into());offsets.insert(change.end, c.end);c.start = ChangePosition(contents_.len().into());contents_.extend(&contents);c.end = ChangePosition(contents_.len().into()); - replacement in libpijul/src/change/text_changes.rs at line 868[7.65893]→[7.65893:66052](∅→∅),[7.66052]→[7.540:600](∅→∅),[7.600]→[7.9665:9712](∅→∅),[7.9712]→[7.66160:66489](∅→∅),[7.66160]→[7.66160:66489](∅→∅),[7.66489]→[7.601:661](∅→∅),[7.661]→[7.9713:9762](∅→∅),[7.9762]→[7.66599:67016](∅→∅),[7.66599]→[7.66599:67016](∅→∅)
Ok(std::mem::replace(current,Some(if &cap[2] == "Solving" {let mut v = default_newvertex();v.inode = parse_pos(changes, &cap[7])?;Hunk::SolveOrderConflict {change: Atom::NewVertex(v),local: Local {path: cap[5].to_string(),line: cap[6].parse().unwrap(),},}} else {let mut v = default_edgemap();v.inode = parse_pos(changes, &cap[7])?;Hunk::UnsolveOrderConflict {change: Atom::EdgeMap(v),local: Local {path: cap[5].to_string(),line: cap[6].parse().unwrap(),},}}),))} else if let Some(cap) = ZOMBIE.captures(h) {if has_newvertices(current) {contents_.push(0)Ok(Hunk::SolveOrderConflict {change: Atom::NewVertex(c),local: Local { path, line },}) - replacement in libpijul/src/change/text_changes.rs at line 873[7.67030]→[7.67030:67073](∅→∅),[7.67073]→[7.662:743](∅→∅),[7.743]→[7.67153:67212](∅→∅),[7.67153]→[7.67153:67212](∅→∅),[7.67212]→[7.9763:9809](∅→∅),[7.9809]→[7.67260:67530](∅→∅),[7.67260]→[7.67260:67530](∅→∅),[7.67530]→[7.4542:4598](∅→∅)
let mut v = default_edgemap();v.inode = parse_pos(changes, &cap.name("inode").unwrap().as_str())?;Ok(std::mem::replace(current,Some(Hunk::ResurrectZombies {change: Atom::EdgeMap(v),local: Local {path: cap.name("path").unwrap().as_str().parse().unwrap(),line: cap.name("line").unwrap().as_str().parse().unwrap(),},encoding: encoding_from_label(cap),PrintableHunk::UnsolveOrderConflict {path,line,pos,encoding: _,change,contents: _,} => Ok(Hunk::UnsolveOrderConflict {change: Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&change, changes)?,inode: from_printable_pos(changes, pos)?, - replacement in libpijul/src/change/text_changes.rs at line 885[7.67550]→[7.67550:67610](∅→∅),[7.67610]→[7.9810:9847](∅→∅),[7.9847]→[7.67649:67725](∅→∅),[7.67649]→[7.67649:67725](∅→∅),[7.67725]→[7.152:182](∅→∅),[7.182]→[7.67725:67925](∅→∅),[7.67725]→[7.67725:67925](∅→∅),[7.67925]→[7.141328:141466](∅→∅),[7.141466]→[7.67925:68022](∅→∅),[7.67925]→[7.67925:68022](∅→∅),[7.68022]→[7.141467:141550](∅→∅),[7.141550]→[7.68103:68233](∅→∅),[7.68103]→[7.68103:68233](∅→∅),[7.68233]→[7.17146:17201](∅→∅),[7.17201]→[7.141551:141629](∅→∅),[7.141629]→[7.68311:68539](∅→∅),[7.68311]→[7.68311:68539](∅→∅),[7.68539]→[7.183:278](∅→∅),[7.278]→[7.68624:68732](∅→∅),[7.68624]→[7.68624:68732](∅→∅),[7.68732]→[7.87:216](∅→∅),[7.216]→[7.21240:21312](∅→∅),[7.21312]→[7.141703:141780](∅→∅),[7.141703]→[7.141703:141780](∅→∅),[7.141780]→[7.378:584](∅→∅),[7.378]→[7.378:584](∅→∅),[7.584]→[7.69085:69999](∅→∅),[7.69085]→[7.69085:69999](∅→∅),[7.69999]→[7.9848:9885](∅→∅),[7.9885]→[7.70038:70156](∅→∅),[7.70038]→[7.70038:70156](∅→∅),[7.70156]→[7.744:812](∅→∅),[7.812]→[7.70223:70904](∅→∅),[7.70223]→[7.70223:70904](∅→∅),[7.70904]→[7.9886:9925](∅→∅),[7.9925]→[7.70945:71065](∅→∅),[7.70945]→[7.70945:71065](∅→∅),[7.71065]→[7.813:881](∅→∅),[7.881]→[7.71132:71815](∅→∅),[7.71132]→[7.71132:71815](∅→∅),[7.71815]→[7.9926:9964](∅→∅),[7.9964]→[7.71855:71968](∅→∅),[7.71855]→[7.71855:71968](∅→∅),[7.71968]→[7.882:950](∅→∅),[7.950]→[7.72035:73304](∅→∅),[7.72035]→[7.72035:73304](∅→∅),[7.73304]→[7.279:426](∅→∅),[7.426]→[7.73367:73590](∅→∅),[7.10026]→[7.73367:73590](∅→∅),[7.73367]→[7.73367:73590](∅→∅),[7.73590]→[7.141781:141868](∅→∅),[7.141868]→[7.73677:73707](∅→∅),[7.73677]→[7.73677:73707](∅→∅),[7.73707]→[7.427:516](∅→∅),[7.516]→[7.73786:73938](∅→∅),[7.73786]→[7.73786:73938](∅→∅),[7.73938]→[7.21313:21413](∅→∅),[7.21413]→[7.313:343](∅→∅),[7.141944]→[7.313:343](∅→∅),[7.313]→[7.313:343](∅→∅),[7.343]→[7.141945:142023](∅→∅),[7.142023]→[7.74101:74748](∅→∅),[7.74101]→[7.74101:74748](∅→∅),[7.74748]→[7.951:1026](∅→∅),[7.1026]→[7.74822:75063](∅→∅),[7.74822]→[7.74822:75063](∅→∅),[7.75063]→[7.10027:10068](∅→∅),[7.10068]→[7.75106:75183](∅→∅),[7.75106]→[7.75106:75183](∅→∅),[7.75183]→[7.517:547](∅→∅),[7.547]→[7.75183:75408](∅→∅),[7.75183]→[7.75183:75408](∅→∅),[7.75408]→[7.142024:142109](∅→∅),[7.142109]→[7.75493:75523](∅→∅),[7.75493]→[7.75493:75523](∅→∅),[7.75523]→[7.548:635](∅→∅),[7.635]→[7.75600:75755](∅→∅),[7.75600]→[7.75600:75755](∅→∅),[7.75755]→[7.21414:21510](∅→∅),[7.21510]→[7.142205:142281](∅→∅),[7.474]→[7.142205:142281](∅→∅),[7.142281]→[7.75914:76569](∅→∅),[7.75914]→[7.75914:76569](∅→∅),[7.76569]→[7.1027:1102](∅→∅),[7.1102]→[7.76643:76884](∅→∅),[7.76643]→[7.76643:76884](∅→∅),[7.76884]→[7.10069:10212](∅→∅),[7.10212]→[7.1103:1171](∅→∅),[7.77031]→[7.1103:1171](∅→∅),[7.1171]→[7.77098:77334](∅→∅),[7.77098]→[7.77098:77334](∅→∅),[7.77334]→[7.10213:10288](∅→∅),[7.10288]→[7.77411:77590](∅→∅),[7.77411]→[7.77411:77590](∅→∅),[7.77590]→[7.142282:142369](∅→∅),[7.142369]→[7.77677:77707](∅→∅),[7.77677]→[7.77677:77707](∅→∅),[7.77707]→[7.636:767](∅→∅),[7.767]→[7.77786:78601](∅→∅),[7.77786]→[7.77786:78601](∅→∅),[7.78601]→[7.142370:142457](∅→∅),[7.142457]→[7.21511:21698](∅→∅),[7.21698]→[7.78831:79025](∅→∅),[7.142565]→[7.78831:79025](∅→∅),[7.78831]→[7.78831:79025](∅→∅),[7.79025]→[7.10289:10366](∅→∅),[7.10366]→[7.1172:1240](∅→∅),[7.79104]→[7.1172:1240](∅→∅),[7.1240]→[7.79171:79387](∅→∅),[7.79171]→[7.79171:79387](∅→∅),[7.79387]→[7.10367:10440](∅→∅),[7.10440]→[7.1241:1309](∅→∅),[7.79462]→[7.1241:1309](∅→∅),[7.1309]→[7.79529:79931](∅→∅),[7.79529]→[7.79529:79931](∅→∅)
))} else {match current {Some(Hunk::FileAdd {ref mut contents,ref mut add_name,encoding,..}) => {if h.starts_with('+') {if contents.is_none() {let mut v = default_newvertex();// The `-1` here comes from the extra 0// padding bytes pushed onto `contents_`.let inode = Position {change: None,pos: ChangePosition((contents_.len() - 1).into()),};v.up_context.push(inode);v.inode = inode;v.flag = EdgeFlags::BLOCK;v.start = ChangePosition(contents_.len().into());*contents = Some(Atom::NewVertex(v));}if let Some(Atom::NewVertex(ref mut contents)) = contents {if h.starts_with('+') {text_changes::parse_line_add(h, contents, contents_, encoding)}}} else if h.starts_with('\\') {if let Some(Atom::NewVertex(mut c)) = contents.take() {if c.end > c.start {if contents_[c.end.us() - 1] == b'\n' {assert_eq!(c.end.us(), contents_.len());contents_.pop();c.end.0 -= 1;}*contents = Some(Atom::NewVertex(c))}}} else if let Some(cap) = CONTEXT.captures(h) {if let Atom::NewVertex(ref mut name) = add_name {name.up_context = parse_pos_vec(changes, offsets, &cap[1])?;if let (Some(new_start), Some(new_end)) = (cap.get(4), cap.get(5)) {offsets.insert(new_start.as_str().parse().unwrap(), name.start);offsets.insert(new_end.as_str().parse().unwrap(), name.end);offsets.insert(new_end.as_str().parse::<u64>().unwrap() + 1,name.end + 1,);}}}Ok(None)}Some(Hunk::FileDel {ref mut del,ref mut contents,..}) => {if let Some(edges) = parse_edges(changes, h)? {if let Atom::EdgeMap(ref mut e) = del {if edges[0].flag.contains(EdgeFlags::FOLDER) {*e = EdgeMap {inode: e.inode,edges,}} else {*contents = Some(Atom::EdgeMap(EdgeMap {inode: e.inode,edges,}))}}}Ok(None)}Some(Hunk::FileUndel {ref mut undel,ref mut contents,..}) => {if let Some(edges) = parse_edges(changes, h)? {if let Atom::EdgeMap(ref mut e) = undel {if edges[0].flag.contains(EdgeFlags::FOLDER) {*e = EdgeMap {inode: e.inode,edges,}} else {*contents = Some(Atom::EdgeMap(EdgeMap {inode: e.inode,edges,}))}}}Ok(None)}Some(Hunk::FileMove {ref mut del,ref mut add,..}) => {if let Some(edges) = parse_edges(changes, h)? {if edges[0].flag.contains(EdgeFlags::DELETED) {*del = Atom::EdgeMap(EdgeMap {inode: del.inode(),edges,});return Ok(None);} else if let Atom::EdgeMap(ref mut add) = add {if add.edges.is_empty() {*add = EdgeMap {inode: add.inode,edges,};return Ok(None);}}} else if let Some(cap) = CONTEXT.captures(h) {if let Atom::NewVertex(ref mut c) = add {debug!("cap = {:?}", cap);c.up_context = parse_pos_vec(changes, offsets, &cap[1])?;if let Some(cap) = cap.get(7) {c.down_context = parse_pos_vec(changes, offsets, cap.as_str())?;}}}Ok(None)}Some(Hunk::Edit {ref mut change,encoding,..}) => {debug!("edit {:?}", h);if h.starts_with("+ ") {if let Atom::NewVertex(ref mut change) = change {if change.start == change.end {change.start = ChangePosition(contents_.len().into());}text_changes::parse_line_add(h, change, contents_, encoding)}} else if h.starts_with('\\') {if let Atom::NewVertex(ref mut change) = change {if change.end > change.start && contents_[change.end.us() - 1] == b'\n'{assert_eq!(change.end.us(), contents_.len());contents_.pop();change.end.0 -= 1;}}} else if let Some(cap) = CONTEXT.captures(h) {if let Atom::NewVertex(ref mut c) = change {debug!("cap = {:?}", cap);c.up_context = parse_pos_vec(changes, offsets, &cap[1])?;if let Some(cap) = cap.get(7) {c.down_context = parse_pos_vec(changes, offsets, cap.as_str())?;}}} else if let Some(edges) = parse_edges(changes, h)? {*change = Atom::EdgeMap(EdgeMap {inode: change.inode(),edges,});}Ok(None)}Some(Hunk::Replacement {ref mut change,ref mut replacement,encoding,..}) => {if h.starts_with("+ ") {if let Atom::NewVertex(ref mut repl) = replacement {if repl.start == repl.end {repl.start = ChangePosition(contents_.len().into());}text_changes::parse_line_add(h, repl, contents_, encoding)}} else if h.starts_with('\\') {if let Atom::NewVertex(ref mut repl) = replacement {if repl.end > repl.start && contents_[repl.end.us() - 1] == b'\n' {assert_eq!(repl.end.us(), contents_.len());contents_.pop();repl.end.0 -= 1;}}} else if let Some(cap) = CONTEXT.captures(h) {debug!("cap = {:?}", cap);if let Atom::NewVertex(ref mut repl) = replacement {repl.up_context = parse_pos_vec(changes, offsets, &cap[1])?;if let Some(cap) = cap.get(7) {repl.down_context = parse_pos_vec(changes, offsets, cap.as_str())?;}}} else if let Some(edges) = parse_edges(changes, h)? {*change = Atom::EdgeMap(EdgeMap {inode: change.inode(),edges,});}Ok(None)}Some(Hunk::SolveNameConflict { ref mut name, .. })| Some(Hunk::UnsolveNameConflict { ref mut name, .. }) => {if let Some(edges) = parse_edges(changes, h)? {*name = Atom::EdgeMap(EdgeMap {edges,inode: name.inode(),})}Ok(None)}Some(Hunk::SolveOrderConflict { ref mut change, .. }) => {if h.starts_with("+ ") {if let Atom::NewVertex(ref mut change) = change {if change.start == change.end {change.start = ChangePosition(contents_.len().into());}// TODO encodingtext_changes::parse_line_add(h, change, contents_, &None)}} else if let Some(cap) = CONTEXT.captures(h) {debug!("cap = {:?}", cap);if let Atom::NewVertex(ref mut change) = change {change.up_context = parse_pos_vec(changes, offsets, &cap[1])?;if let Some(cap) = cap.get(7) {change.down_context =parse_pos_vec(changes, offsets, cap.as_str())?;}if let (Some(new_start), Some(new_end)) = (cap.get(4), cap.get(5)) {let new_start = new_start.as_str().parse::<u64>().unwrap();let new_end = new_end.as_str().parse::<u64>().unwrap();change.start = ChangePosition(contents_.len().into());change.end = ChangePosition((contents_.len() as u64 + new_end - new_start).into(),);offsets.insert(new_end, change.end);}}}Ok(None)}Some(Hunk::UnsolveOrderConflict { ref mut change, .. }) => {if let Some(edges) = parse_edges(changes, h)? {if let Atom::EdgeMap(ref mut change) = change {change.edges = edges}}Ok(None)}Some(Hunk::ResurrectZombies { ref mut change, .. }) => {if let Some(edges) = parse_edges(changes, h)? {if let Atom::EdgeMap(ref mut change) = change {change.edges = edges}}Ok(None)}None => {debug!("current = {:#?}", current);debug!("h = {:?}", h);Ok(None)}}local: Local { path, line },}),PrintableHunk::ResurrectZombies {path,line,pos,encoding,change,contents: _,} => Ok(Hunk::ResurrectZombies {change: Atom::EdgeMap(EdgeMap {edges: from_printable_edge_map(&change, changes)?,inode: from_printable_pos(changes, pos)?,}),local: Local { path, line },encoding,}), - edit in libpijul/src/change/text_changes.rs at line 906
fn encoding_from_label(cap: Captures) -> Option<Encoding> {let encoding_label = cap.name("encoding").unwrap().as_str();if encoding_label != BINARY_LABEL {Some(Encoding::for_label(encoding_label))} else {None}}lazy_static! {static ref POS: regex::Regex = regex::Regex::new(r#"(\d+)\.(\d+)"#).unwrap();static ref EDGE: regex::Regex =regex::Regex::new(r#"\s*(?P<prev>[BFD]*):(?P<flag>[BFD]*)\s+(?P<up_c>\d+)\.(?P<up_l>\d+)\s*->\s*(?P<c>\d+)\.(?P<l0>\d+):(?P<l1>\d+)/(?P<intro>\d+)\s*"#).unwrap();} - edit in libpijul/src/change/text_changes.rs at line 913[7.80495]→[7.80495:80559](∅→∅),[7.80559]→[7.142643:142684](∅→∅),[7.142684]→[7.80595:80708](∅→∅),[7.80595]→[7.80595:80708](∅→∅)
inode: Position {change: Some(Hash::None),pos: ChangePosition(L64(0)),},}}pub fn default_edgemap() -> EdgeMap<Option<Hash>> {EdgeMap {edges: Vec::new(), - replacement in libpijul/src/change/text_changes.rs at line 920[7.80828]→[7.10441:10518](∅→∅),[7.10518]→[7.80907:80927](∅→∅),[7.80907]→[7.80907:80927](∅→∅),[7.80927]→[7.10519:10587](∅→∅),[7.10587]→[7.80997:81102](∅→∅),[7.80997]→[7.80997:81102](∅→∅)
pub fn has_newvertices<L>(current: &Option<Hunk<Option<Hash>, L>>) -> bool {match current {Some(Hunk::FileAdd { contents: None, .. }) | None => false,Some(rec) => rec.iter().any(|e| matches!(e, Atom::NewVertex(_))),}}pub fn parse_pos_vec(// TODO: renamepub fn from_printable_pos_vec_offsets( - replacement in libpijul/src/change/text_changes.rs at line 924
s: &str,s: &[PrintablePos], - replacement in libpijul/src/change/text_changes.rs at line 927
for pos in POS.captures_iter(s) {let change: usize = (&pos[1]).parse().unwrap();let pos: u64 = (&pos[2]).parse().unwrap();let pos = if change == 0 {for PrintablePos(change, pos) in s {let pos = if *change == 0 { - replacement in libpijul/src/change/text_changes.rs at line 933
return Err(TextDeError::MissingPosition(pos));return Err(TextDeError::MissingPosition(*pos)); - replacement in libpijul/src/change/text_changes.rs at line 939
change: change_ref(changes, change)?,change: change_ref(changes, *change)?, - replacement in libpijul/src/change/text_changes.rs at line 959
pub fn parse_pos(pub fn from_printable_pos( - replacement in libpijul/src/change/text_changes.rs at line 961
s: &str,pos: PrintablePos, - edit in libpijul/src/change/text_changes.rs at line 963
let pos = POS.captures(s).unwrap();let change: usize = (&pos[1]).parse().unwrap();let pos: u64 = (&pos[2]).parse().unwrap(); - replacement in libpijul/src/change/text_changes.rs at line 964
change: change_ref(changes, change)?,pos: ChangePosition(L64(pos.to_le())),change: change_ref(changes, pos.0)?,pos: ChangePosition(L64(pos.1.to_le())), - replacement in libpijul/src/change/text_changes.rs at line 969
pub fn parse_edges(pub fn from_printable_pos_vec( - replacement in libpijul/src/change/text_changes.rs at line 971[7.1943]→[7.1943:2019](∅→∅),[7.2019]→[7.82596:83547](∅→∅),[7.82596]→[7.82596:83547](∅→∅),[7.83547]→[7.2020:2079](∅→∅),[7.2079]→[7.142821:142881](∅→∅),[7.142881]→[7.83652:83700](∅→∅),[7.83652]→[7.83652:83700](∅→∅),[7.83700]→[7.2080:2139](∅→∅),[7.2139]→[7.142882:143006](∅→∅),[7.143006]→[7.83856:83875](∅→∅),[7.83856]→[7.83856:83875](∅→∅),[7.83875]→[7.2140:2208](∅→∅),[7.2208]→[7.83942:84008](∅→∅),[7.83942]→[7.83942:84008](∅→∅),[7.84008]→[7.2209:2238](∅→∅),[7.2238]→[7.84033:84043](∅→∅),[7.84033]→[7.84033:84043](∅→∅)
s: &str,) -> Result<Option<Vec<NewEdge<Option<Hash>>>>, TextDeError> {debug!("parse_edges {:?}", s);let mut result = Vec::new();for edge in s.split(',') {debug!("parse edge {:?}", edge);if let Some(cap) = EDGE.captures(edge) {let previous = read_flag(cap.name("prev").unwrap().as_str());let flag = read_flag(cap.name("flag").unwrap().as_str());let change0: usize = cap.name("up_c").unwrap().as_str().parse().unwrap();let pos0: u64 = cap.name("up_l").unwrap().as_str().parse().unwrap();let change1: usize = cap.name("c").unwrap().as_str().parse().unwrap();let start1: u64 = cap.name("l0").unwrap().as_str().parse().unwrap();let end1: u64 = cap.name("l1").unwrap().as_str().parse().unwrap();let introduced_by: usize = cap.name("intro").unwrap().as_str().parse().unwrap();result.push(NewEdge {previous,flag,from: Position {change: change_ref(changes, change0)?,pos: ChangePosition(L64(pos0.to_le())),},to: Vertex {change: change_ref(changes, change1)?,start: ChangePosition(L64(start1.to_le())),end: ChangePosition(L64(end1.to_le())),},introduced_by: change_ref(changes, introduced_by)?,})} else {debug!("not parsed");return Ok(None);}pos: &[PrintablePos],) -> Result<Vec<Position<Option<Hash>>>, TextDeError> {let mut buf = Vec::new();for p in pos {buf.push(from_printable_pos(changes, *p)?); - replacement in libpijul/src/change/text_changes.rs at line 977
Ok(Some(result))Ok(buf) - edit in libpijul/src/change/text_changes.rs at line 980[7.84069]→[7.768:987](∅→∅),[7.987]→[7.1204:1262](∅→∅),[7.1262]→[7.1033:1040](∅→∅),[7.1033]→[7.1033:1040](∅→∅),[7.1040]→[7.84191:84540](∅→∅),[7.84191]→[7.84191:84540](∅→∅),[7.84540]→[7.143007:143064](∅→∅),[7.143064]→[7.84597:84600](∅→∅),[7.84597]→[7.84597:84600](∅→∅)
pub fn parse_line_add(h: &str,change: &mut NewVertex<Option<Hash>>,contents_: &mut Vec<u8>,encoding: &Option<Encoding>,) {let h = match encoding {Some(encoding) => encoding.encode(h),None => std::borrow::Cow::Borrowed(h.as_bytes()),};debug!("parse_line_add {:?} {:?}", change.end, change.start);debug!("parse_line_add {:?}", h);if h.len() > 2 {let h = &h[2..h.len()];contents_.extend(h);} else if h.len() > 1 {contents_.push(b'\n');}debug!("contents_.len() = {:?}", contents_.len());trace!("contents_ = {:?}", contents_);change.end = ChangePosition(contents_.len().into());} - replacement in libpijul/src/change/text_changes.rs at line 989
write!(self, "{}b{}", pref, data_encoding::BASE64.encode(contents))writeln!(self, "{}b{}", pref, data_encoding::BASE64.encode(contents)) - edit in libpijul/src/change/text_changes.rs at line 996[7.143163]→[7.3825:3869](∅→∅),[7.3825]→[7.3825:3869](∅→∅),[7.3869]→[7.84642:84694](∅→∅),[7.84642]→[7.84642:84694](∅→∅),[7.84694]→[7.449:482](∅→∅),[7.482]→[7.84694:84728](∅→∅),[7.84694]→[7.84694:84728](∅→∅),[7.84728]→[7.483:522](∅→∅),[7.522]→[7.1263:1466](∅→∅),[7.1466]→[7.1236:1278](∅→∅),[7.6169]→[7.1236:1278](∅→∅),[7.591]→[7.1236:1278](∅→∅),[7.1278]→[7.85088:85206](∅→∅),[7.3962]→[7.85088:85206](∅→∅),[7.5848]→[7.85088:85206](∅→∅),[7.85088]→[7.85088:85206](∅→∅)
pub fn print_contents<W: WriteChangeLine>(w: &mut W,pref: &str,contents: &[u8],encoding: &Option<Encoding>,) -> Result<(), std::io::Error> {if let Some(encoding) = encoding {let dec = encoding.decode(&contents);let dec = if dec.ends_with("\n") {&dec[..dec.len() - 1]} else {&dec};for a in dec.split('\n') {w.write_change_line(pref, a)?}} else {writeln!(w, "{}b{}", pref, data_encoding::BASE64.encode(contents))?}Ok(())} - replacement in libpijul/src/change/text_changes.rs at line 997
pub fn print_change_contents<W: WriteChangeLine, C: ChangeStore>(w: &mut W,pub fn get_change_contents<C: ChangeStore>( - replacement in libpijul/src/change/text_changes.rs at line 1001[7.85365]→[7.4850:4883](∅→∅),[7.4883]→[7.85365:85407](∅→∅),[7.85365]→[7.85365:85407](∅→∅),[7.85407]→[7.6170:6220](∅→∅)
encoding: &Option<Encoding>,) -> Result<(), TextSerError<C::Error>> {debug!("print_change_contents {:?}", change);) -> Result<Vec<u8>, TextSerError<C::Error>> {debug!("get_change_contents {:?}", change); - replacement in libpijul/src/change/text_changes.rs at line 1004[7.85426]→[7.85426:85462](∅→∅),[7.85462]→[7.143164:143228](∅→∅),[7.143228]→[7.4884:4934](∅→∅),[7.639]→[7.85578:85615](∅→∅),[7.4934]→[7.85578:85615](∅→∅),[7.85578]→[7.85578:85615](∅→∅),[7.85615]→[7.6221:6278](∅→∅),[7.6278]→[7.169:206](∅→∅),[7.85615]→[7.169:206](∅→∅),[7.206]→[7.85650:85693](∅→∅),[7.85650]→[7.85650:85693](∅→∅),[7.85693]→[7.528:623](∅→∅)
Atom::NewVertex(ref n) => {let c = &change_contents[n.start.us()..n.end.us()];print_contents(w, "+", c, encoding)?;if !c.ends_with(b"\n") {debug!("print_change_contents {:?}", c);writeln!(w, "\n\\")?}Ok(())}Atom::EdgeMap(ref n) if n.edges.is_empty() => return Err(TextSerError::InvalidChange),Atom::NewVertex(ref n) => Ok(change_contents[n.start.us()..n.end.us()].to_vec()),Atom::EdgeMap(ref n) if n.edges.is_empty() => Err(TextSerError::InvalidChange), - edit in libpijul/src/change/text_changes.rs at line 1007
// TODO: get rid of `tmp` and/or `buf` - edit in libpijul/src/change/text_changes.rs at line 1009
let mut tmp = Vec::new(); - replacement in libpijul/src/change/text_changes.rs at line 1015
buf.clear();tmp.clear(); - replacement in libpijul/src/change/text_changes.rs at line 1017
.get_contents_ext(e.to, &mut buf).get_contents_ext(e.to, &mut tmp) - replacement in libpijul/src/change/text_changes.rs at line 1019[7.86133]→[7.4935:4997](∅→∅),[7.443]→[7.0:43](∅→∅),[7.4997]→[7.0:43](∅→∅),[7.86184]→[7.0:43](∅→∅),[7.43]→[7.6279:6342](∅→∅),[7.6342]→[7.43:95](∅→∅),[7.43]→[7.43:95](∅→∅)
print_contents(w, "-", &buf[..], &encoding)?;if !buf.ends_with(b"\n") {debug!("print_change_contents {:?}", buf);writeln!(w)?;}buf.extend_from_slice(&tmp); - replacement in libpijul/src/change/text_changes.rs at line 1022
Ok(())Ok(buf) - replacement in libpijul/src/change/text_changes.rs at line 1024
_ => Ok(()),_ => Ok(Vec::new()), - replacement in libpijul/src/change/text_changes.rs at line 1028
pub fn write_deleted_names<W: std::io::Write, C: ChangeStore>(w: &mut W,pub fn get_deleted_names<C: ChangeStore>( - replacement in libpijul/src/change/text_changes.rs at line 1031
) -> Result<(), TextSerError<C::Error>> {) -> Result<Vec<String>, TextSerError<C::Error>> {let mut res = Vec::new(); - replacement in libpijul/src/change/text_changes.rs at line 1034
let mut buf = Vec::new();let mut is_first = true;let mut tmp = Vec::new(); - replacement in libpijul/src/change/text_changes.rs at line 1036
buf.clear();tmp.clear(); - replacement in libpijul/src/change/text_changes.rs at line 1038
.get_contents_ext(d.to, &mut buf).get_contents_ext(d.to, &mut tmp) - replacement in libpijul/src/change/text_changes.rs at line 1040[7.86741]→[7.86741:86774](∅→∅),[7.86774]→[7.3390:3474](∅→∅),[7.3474]→[7.86850:86962](∅→∅),[7.86850]→[7.86850:86962](∅→∅)
if !buf.is_empty() {let FileMetadata { basename: name, .. } = FileMetadata::read(&buf);write!(w, "{}{:?}", if is_first { "" } else { ", " }, name)?;is_first = false;if !tmp.is_empty() {let FileMetadata { basename: name, .. } = FileMetadata::read(&tmp);res.push(name.to_string()); - replacement in libpijul/src/change/text_changes.rs at line 1046
Ok(())Ok(res) - replacement in libpijul/src/change/text_changes.rs at line 1049
pub fn write_flag<W: std::io::Write>(mut w: W, flag: EdgeFlags) -> Result<(), std::io::Error> {if flag.contains(EdgeFlags::BLOCK) {w.write_all(b"B")?;}if flag.contains(EdgeFlags::FOLDER) {w.write_all(b"F")?;}if flag.contains(EdgeFlags::DELETED) {w.write_all(b"D")?;}assert!(!flag.contains(EdgeFlags::PARENT));assert!(!flag.contains(EdgeFlags::PSEUDO));Ok(())}pub fn read_flag(s: &str) -> EdgeFlags {let mut f = EdgeFlags::empty();for i in s.chars() {match i {'B' => f |= EdgeFlags::BLOCK,'F' => f |= EdgeFlags::FOLDER,'D' => f |= EdgeFlags::DELETED,c => panic!("read_flag: {:?}", c),}}f}pub fn write_pos<W: std::io::Write>(mut w: W,pub fn to_printable_pos( - replacement in libpijul/src/change/text_changes.rs at line 1052
) -> Result<(), std::io::Error> {) -> PrintablePos { - replacement in libpijul/src/change/text_changes.rs at line 1060
write!(w, "{}.{}", change, pos.pos.0)?;Ok(())PrintablePos(change, pos.pos.0 .0) - replacement in libpijul/src/change/text_changes.rs at line 1063
pub fn write_atom<W: std::io::Write>(w: &mut W,pub fn to_printable_pos_vec( - replacement in libpijul/src/change/text_changes.rs at line 1065
atom: &Atom<Option<Hash>>,) -> Result<(), std::io::Error> {match atom {Atom::NewVertex(ref n) => write_newvertex(w, hashes, n),Atom::EdgeMap(ref n) => write_edgemap(w, hashes, n),}pos: &[Position<Option<Hash>>],) -> Vec<PrintablePos> {pos.iter().map(|c| to_printable_pos(hashes, *c)).collect() - edit in libpijul/src/change/text_changes.rs at line 1069[7.88449]→[7.88449:89960](∅→∅),[7.89960]→[7.10588:10689](∅→∅),[7.10689]→[7.90065:90121](∅→∅),[7.90065]→[7.90065:90121](∅→∅)
pub fn write_newvertex<W: std::io::Write>(mut w: W,hashes: &HashMap<Hash, usize>,n: &NewVertex<Option<Hash>>,) -> Result<(), std::io::Error> {write!(w, " up")?;for c in n.up_context.iter() {write!(w, " ")?;write_pos(&mut w, hashes, *c)?}write!(w, ", new {}:{}", n.start.0, n.end.0)?;if !n.down_context.is_empty() {write!(w, ", down")?;for c in n.down_context.iter() {write!(w, " ")?;write_pos(&mut w, hashes, *c)?}}w.write_all(b"\n")?;Ok(())}pub fn write_edgemap<W: std::io::Write>(mut w: W,hashes: &HashMap<Hash, usize>,n: &EdgeMap<Option<Hash>>,) -> Result<(), std::io::Error> {let mut is_first = true;for c in n.edges.iter() {if !is_first {write!(w, ", ")?;}is_first = false;write_flag(&mut w, c.previous)?;write!(w, ":")?;write_flag(&mut w, c.flag)?;write!(w, " ")?;write_pos(&mut w, hashes, c.from)?;write!(w, " -> ")?;write_pos(&mut w, hashes, c.to.start_pos())?;let h = if let Some(h) = hashes.get(c.introduced_by.as_ref().unwrap()) {h} else {panic!("introduced_by = {:?}, not found", c.introduced_by);};write!(w, ":{}/{}", c.to.end.0, h)?;}writeln!(w)?;Ok(())}#[derive(Debug, Clone, PartialEq, Eq)]pub enum Section {Header(String),Deps,Changes {changes: Vec<Hunk<Option<Hash>, Local>>,current: Option<Hunk<Option<Hash>, Local>>,offsets: HashMap<u64, ChangePosition>,},} - file addition: printable.rs[49.931000]
use std::fmt;#[cfg(test)]use quickcheck::{Arbitrary, Gen};#[cfg(test)]use PrintableHunk::*;use super::*;#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum StringFragment<'a> {Literal(&'a str),EscapedChar(char),}#[derive(Copy, Clone, Debug, Eq, PartialEq)]pub enum PrintablePerms {IsDir,IsExecutable,IsFile,}#[derive(Copy, Clone, Debug, Eq, PartialEq)]pub struct PrintableEdgeFlags {pub block: bool,pub folder: bool,pub deleted: bool,}#[derive(Clone, Debug, Eq, PartialEq)]pub struct PrintableEdge {pub previous: PrintableEdgeFlags,pub flag: PrintableEdgeFlags,pub from: PrintablePos,pub to_start: PrintablePos,pub to_end: u64,pub introduced_by: usize,}#[derive(Clone, Debug, Eq, PartialEq)]pub struct PrintableNewVertex {pub up_context: Vec<PrintablePos>,pub start: u64,pub end: u64,pub down_context: Vec<PrintablePos>,}#[derive(Clone, Debug, Eq, PartialEq)]pub enum PrintableAtom {NewVertex(PrintableNewVertex),Edges(Vec<PrintableEdge>),}#[derive(Copy, Clone, Debug, Eq, PartialEq)]pub struct PrintablePos(pub usize, pub u64);// TODO: Some of these are untested: I didn't know how to create them from the// command line// NOTE: contents must be valid under the chosen encoding#[derive(PartialEq, Eq, Clone, Debug)]pub enum PrintableHunk {FileMoveV {path: String,name: String,perms: PrintablePerms,pos: PrintablePos,up_context: Vec<PrintablePos>,down_context: Vec<PrintablePos>,del: Vec<PrintableEdge>,},FileMoveE {path: String,pos: PrintablePos,add: Vec<PrintableEdge>,del: Vec<PrintableEdge>,},FileAddition {name: String,parent: String,perms: PrintablePerms,encoding: Option<Encoding>,up_context: Vec<PrintablePos>,start: u64,end: u64,contents: Vec<u8>,},FileDel {path: String,pos: PrintablePos,encoding: Option<Encoding>,del_edges: Vec<PrintableEdge>,content_edges: Vec<PrintableEdge>,contents: Vec<u8>,},FileUndel {path: String,pos: PrintablePos,encoding: Option<Encoding>,undel_edges: Vec<PrintableEdge>,content_edges: Vec<PrintableEdge>,contents: Vec<u8>,},Edit {path: String,line: usize,pos: PrintablePos,encoding: Option<Encoding>,change: PrintableAtom,contents: Vec<u8>,},Replace {path: String,line: usize,pos: PrintablePos,encoding: Option<Encoding>,change: Vec<PrintableEdge>,replacement: PrintableNewVertex,change_contents: Vec<u8>,replacement_contents: Vec<u8>,},SolveNameConflict {path: String,pos: PrintablePos,names: Vec<String>,edges: Vec<PrintableEdge>,},UnsolveNameConflict {path: String,pos: PrintablePos,names: Vec<String>,edges: Vec<PrintableEdge>,},SolveOrderConflict {path: String,line: usize,pos: PrintablePos,encoding: Option<Encoding>,change: PrintableNewVertex,contents: Vec<u8>,},UnsolveOrderConflict {path: String,line: usize,pos: PrintablePos,encoding: Option<Encoding>,change: Vec<PrintableEdge>,contents: Vec<u8>,},ResurrectZombies {path: String,line: usize,pos: PrintablePos,encoding: Option<Encoding>,change: Vec<PrintableEdge>,contents: Vec<u8>,},}#[derive(PartialEq, Eq, Clone, Debug)]pub struct PrintableDep {pub type_: DepType,pub hash: String,}// TODO: make names more precise. I don't know what these should be named.#[derive(PartialEq, Eq, Clone, Debug)]pub enum DepType {Numbered(usize, bool), // (number, plus-sign)ExtraKnown,ExtraUnknown,}pub struct Escaped<'a>(pub &'a str);impl<'a> fmt::Display for Escaped<'a> {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {write!(fmt, "\"")?;for c in self.0.chars() {if c == '\n' {write!(fmt, "\\n")?} else if c == '\r' {write!(fmt, "\\r")?} else if c == '\t' {write!(fmt, "\\t")?} else if c == '\u{08}' {write!(fmt, "\\b")?} else if c == '\u{0C}' {write!(fmt, "\\f")?} else if c == '\\' {write!(fmt, "\\\\")?} else if c == '"' {write!(fmt, "\\\"")?} else {write!(fmt, "{}", c)?}}write!(fmt, "\"")?;Ok(())}}impl PrintablePerms {pub fn from_metadata(perms: InodeMetadata) -> Self {if perms.0 & 0o1000 == 0o1000 {PrintablePerms::IsDir} else if perms.0 & 0o100 == 0o100 {PrintablePerms::IsExecutable} else {PrintablePerms::IsFile}}pub fn to_metadata(self) -> InodeMetadata {InodeMetadata(match self {PrintablePerms::IsDir => 0o1100,PrintablePerms::IsExecutable => 0o100,PrintablePerms::IsFile => 0o0,})}}impl fmt::Display for PrintablePos {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {write!(fmt, "{}.{}", self.0, self.1)}}impl fmt::Display for PrintablePerms {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {write!(fmt,"{}",match self {PrintablePerms::IsDir => " +dx",PrintablePerms::IsExecutable => " +x",PrintablePerms::IsFile => "",})}}impl PrintableEdgeFlags {pub fn from(ef: EdgeFlags) -> Self {assert!(!ef.contains(EdgeFlags::PARENT));assert!(!ef.contains(EdgeFlags::PSEUDO));Self {block: ef.contains(EdgeFlags::BLOCK),folder: ef.contains(EdgeFlags::FOLDER),deleted: ef.contains(EdgeFlags::DELETED),}}// TODO: make this nicerpub fn to(self) -> EdgeFlags {let mut f = EdgeFlags::empty();if self.block {f |= EdgeFlags::BLOCK;}if self.folder {f |= EdgeFlags::FOLDER;}if self.deleted {f |= EdgeFlags::DELETED;}f}}impl fmt::Display for PrintableEdgeFlags {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {if self.block {write!(fmt, "B")?;}if self.folder {write!(fmt, "F")?;}if self.deleted {write!(fmt, "D")?;}Ok(())}}impl fmt::Display for PrintableEdge {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {write!(fmt,"{}:{} {} -> {}:{}/{}",self.previous, self.flag, self.from, self.to_start, self.to_end, self.introduced_by)}}impl fmt::Display for PrintableNewVertex {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {write!(fmt, " up")?;for c in self.up_context.iter() {write!(fmt, " {}", c)?}write!(fmt, ", new {}:{}, down", self.start, self.end)?;for c in self.down_context.iter() {write!(fmt, " {}", c)?;}Ok(())}}impl fmt::Display for PrintableAtom {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {match self {PrintableAtom::NewVertex(x) => write!(fmt, "{}", x),PrintableAtom::Edges(x) => {for (i, edge) in x.iter().enumerate() {if i > 0 {write!(fmt, ", ")?;}write!(fmt, "{}", edge)?;}Ok(())}}}}impl PrintableHunk {pub fn write<W: WriteChangeLine>(&self, w: &mut W) -> Result<(), std::io::Error> {use PrintableHunk::*;match self {FileMoveV {path,name,perms,pos,up_context,down_context,del,} => {writeln!(w,"Moved: {} {} {} {}",Escaped(path),Escaped(name),perms,pos,)?;writeln!(w, "{}", PrintableAtom::Edges(del.to_vec()))?;write!(w, "up")?;for c in up_context.iter() {write!(w, " {}", c)?}write!(w, ", down")?;for c in down_context.iter() {write!(w, " {}", c)?}writeln!(w)?;}FileMoveE {path,pos,add,del,} => {writeln!(w, "Moved: {} {}", Escaped(path), pos)?;writeln!(w, "{}", PrintableAtom::Edges(add.to_vec()))?;writeln!(w, "{}", PrintableAtom::Edges(del.to_vec()))?;}FileAddition {name,parent,perms,encoding,up_context,start,end,contents,} => {write!(w,"File addition: {} in {}{} {}\n up",Escaped(name),Escaped(parent),perms,Escaped(encoding_label(encoding)),)?;for c in up_context.iter() {write!(w, " {}", c)?}writeln!(w, ", new {}:{}", start, end)?;print_contents(w, "+", contents, encoding)?;}FileDel {path,pos,encoding,del_edges,content_edges,contents,} => {writeln!(w,"File deletion: {} {} {}",Escaped(path),pos,Escaped(encoding_label(encoding)),)?;writeln!(w, "{}", PrintableAtom::Edges(del_edges.to_vec()))?;writeln!(w, "{}", PrintableAtom::Edges(content_edges.to_vec()))?;print_contents(w, "-", contents, encoding)?;}FileUndel {path,pos,encoding,undel_edges,content_edges,contents,} => {writeln!(w,"File un-deletion: {} {} {}",Escaped(path),pos,Escaped(encoding_label(encoding)),)?;writeln!(w, "{}", PrintableAtom::Edges(undel_edges.to_vec()))?;writeln!(w, "{}", PrintableAtom::Edges(content_edges.to_vec()))?;print_contents(w, "+", contents, encoding)?;}Edit {path,line,pos,encoding,change,contents,} => {writeln!(w,"Edit in {}:{} {} {}",Escaped(&path),line,pos,Escaped(encoding_label(encoding)))?;writeln!(w, "{}", change)?;print_contents(w, "+", contents, encoding)?;}Replace {path,line,pos,encoding,change,replacement,change_contents,replacement_contents,} => {writeln!(w,"Replacement in {}:{} {} {}",Escaped(&path),line,pos,Escaped(encoding_label(encoding)))?;writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;writeln!(w, "{}", PrintableAtom::NewVertex(replacement.clone()))?;print_contents(w, "-", change_contents, encoding)?;print_contents(w, "+", replacement_contents, encoding)?;}SolveNameConflict {path,pos,names,edges,} => {write!(w, "Solving a name conflict in {} {}: ", Escaped(path), pos,)?;write_names(w, names)?;writeln!(w)?;writeln!(w, "{}", PrintableAtom::Edges(edges.clone()))?;}UnsolveNameConflict {path,pos,names,edges,} => {write!(w,"Un-solving a name conflict in {} {}: ",Escaped(path),pos,)?;write_names(w, names)?;writeln!(w)?;writeln!(w, "{}", PrintableAtom::Edges(edges.clone()))?;}SolveOrderConflict {path,line,pos,encoding,change,contents,} => {writeln!(w,"Solving an order conflict in {}:{} {} {}",Escaped(path),line,pos,Escaped(encoding_label(encoding)),)?;writeln!(w, "{}", change)?;print_contents(w, "+", contents, encoding)?;}UnsolveOrderConflict {path,line,pos,encoding,change,contents,} => {writeln!(w,"Un-solving an order conflict in {}:{} {} {}",Escaped(path),line,pos,Escaped(encoding_label(encoding)))?;writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;print_contents(w, "-", contents, encoding)?;}ResurrectZombies {path,line,pos,encoding,change,contents,} => {writeln!(w,"Resurrecting zombie lines in {}:{} {} {}",Escaped(path),line,pos,Escaped(encoding_label(encoding)))?;writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;print_contents(w, "+", contents, encoding)?;}};Ok(())}}pub fn write_names<W: std::io::Write>(w: &mut W, names: &[String]) -> Result<(), std::io::Error> {for (i, name) in names.iter().enumerate() {if i > 0 {write!(w, ", ")?;}write!(w, "{}", Escaped(name))?;}Ok(())}pub fn get_encoding(contents: &[u8]) -> Option<Encoding> {let mut detector = crate::chardetng::EncodingDetector::new();detector.feed(contents, true);if let Some(e) = detector.get_valid(None, true, &contents) {Some(Encoding(e))} else {None}// let (encoding_guess, may_be_right) = detector.guess_assess(None, true);// if may_be_right {// Some(Encoding(encoding_guess))// } else {// None// }}fn print_contents<W: WriteChangeLine>(w: &mut W,prefix: &str,contents: &[u8],encoding: &Option<Encoding>,) -> Result<(), std::io::Error> {if let Some(encoding) = encoding {let dec = encoding.decode(&contents);let ends_with_newline = dec.ends_with("\n");let dec = if ends_with_newline {&dec[..dec.len() - 1]} else {&dec};for a in dec.split('\n') {writeln!(w, "{} {}", prefix, a)?;}if !ends_with_newline {writeln!(w, "\\")?;}Ok(())} else {writeln!(w, "{}b{}", prefix, data_encoding::BASE64.encode(contents))}}// QuickCheck instances#[cfg(test)]#[rustfmt::skip]// This may be nicer if it was generated by a macroimpl Arbitrary for PrintableHunk {fn arbitrary(g: &mut Gen) -> Self {fn f<A: Arbitrary>(g: &mut Gen) -> A {Arbitrary::arbitrary(g)}fix_encoding(Gen::new(g.size()).choose(&[FileMoveV {path: f(g), name: f(g), perms: f(g), pos: f(g), up_context: f(g), down_context: f(g), del: f(g),},FileMoveE {path: f(g), pos: f(g), add: f(g), del: f(g),},FileAddition {name: f(g), parent: f(g), perms: f(g), encoding: f(g), up_context: f(g), start: f(g), end: f(g), contents: f(g),},FileDel {path: f(g), pos: f(g), encoding: f(g), del_edges: f(g), content_edges: f(g), contents: f(g),},FileUndel {path: f(g), pos: f(g), encoding: f(g), undel_edges: f(g), content_edges: f(g), contents: f(g),},Edit {path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),},Replace {path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), replacement: f(g), change_contents: f(g), replacement_contents: f(g),},SolveNameConflict {path: f(g), pos: f(g), names: f(g), edges: f(g),},UnsolveNameConflict {path: f(g), pos: f(g), names: f(g), edges: f(g),},SolveOrderConflict {path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),},UnsolveOrderConflict {path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),},ResurrectZombies {path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),},]).unwrap().clone())}// Shrinking frequently blows stack. Investigate how to fix it.// You can disable shrinking by commenting out this function.// This may be best solved by switching to proptest crate/*fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {match self.clone() {FileMoveV { path, name, perms, pos, up_context, down_context, del } =>Box::new((path, name, perms, pos, up_context, down_context, del).shrink().map(|(path, name, perms, pos, up_context, down_context, del)|fix_encoding(FileMoveV { path, name, perms, pos, up_context, down_context, del }))),FileMoveE { path, pos, add, del } =>Box::new((path, pos, add, del).shrink().map(|(path, pos, add, del)|fix_encoding(FileMoveE { path, pos, add, del }))),FileAddition { name, parent, perms, encoding, up_context, start, end, contents } =>Box::new((name, parent, perms, encoding, up_context, start, end, contents).shrink().map(|(name, parent, perms, encoding, up_context, start, end, contents)|fix_encoding(FileAddition { name, parent, perms, encoding, up_context, start, end, contents }))),FileDel { path, pos, encoding, del_edges, content_edges, contents } =>Box::new((path, pos, encoding, del_edges, content_edges, contents).shrink().map(|(path, pos, encoding, del_edges, content_edges, contents)|fix_encoding(FileDel { path, pos, encoding, del_edges, content_edges, contents }))),FileUndel { path, pos, encoding, undel_edges, content_edges, contents } =>Box::new((path, pos, encoding, undel_edges, content_edges, contents).shrink().map(|(path, pos, encoding, undel_edges, content_edges, contents)|fix_encoding(FileUndel { path, pos, encoding, undel_edges, content_edges, contents }))),Edit { path, line, pos, encoding, change, contents } =>Box::new((path, line, pos, encoding, change, contents).shrink().map(|(path, line, pos, encoding, change, contents)|fix_encoding(Edit { path, line, pos, encoding, change, contents }))),Replace { path, line, pos, encoding, change, replacement, change_contents, replacement_contents } =>Box::new((path, line, pos, encoding, change, replacement, change_contents, replacement_contents).shrink().map(|(path, line, pos, encoding, change, replacement, change_contents, replacement_contents)|fix_encoding(Replace { path, line, pos, encoding, change, replacement, change_contents, replacement_contents }))),SolveNameConflict { path, pos, names, edges } =>Box::new((path, pos, names, edges).shrink().map(|(path, pos, names, edges)|fix_encoding(SolveNameConflict { path, pos, names, edges }))),UnsolveNameConflict { path, pos, names, edges } =>Box::new((path, pos, names, edges).shrink().map(|(path, pos, names, edges)|fix_encoding(UnsolveNameConflict { path, pos, names, edges }))),SolveOrderConflict { path, line, pos, encoding, change, contents } =>Box::new((path, line, pos, encoding, change, contents).shrink().map(|(path, line, pos, encoding, change, contents)|fix_encoding(SolveOrderConflict { path, line, pos, encoding, change, contents }))),UnsolveOrderConflict { path, line, pos, encoding, change, contents } =>Box::new((path, line, pos, encoding, change, contents).shrink().map(|(path, line, pos, encoding, change, contents)|fix_encoding(UnsolveOrderConflict { path, line, pos, encoding, change, contents }))),ResurrectZombies { path, line, pos, encoding, change, contents } =>Box::new((path, line, pos, encoding, change, contents).shrink().map(|(path, line, pos, encoding, change, contents)|fix_encoding(ResurrectZombies { path, line, pos, encoding, change, contents }))),}}*/}#[cfg(test)]/// This is the one thing that is not normalized on PrintableHunk: encoding/// content must be valid, given the encoding. This function ensures that.fn fix_encoding(mut hunk: PrintableHunk) -> PrintableHunk {// let mut h = hunk.clone();match &mut hunk {FileAddition {encoding, contents, ..} => *encoding = get_encoding(contents),FileDel {encoding, contents, ..} => *encoding = get_encoding(contents),FileUndel {encoding, contents, ..} => *encoding = get_encoding(contents),Edit {encoding, contents, ..} => *encoding = get_encoding(contents),Replace { encoding, .. } => *encoding = None,SolveOrderConflict {encoding, contents, ..} => *encoding = get_encoding(contents),UnsolveOrderConflict {encoding, contents, ..} => *encoding = get_encoding(contents),ResurrectZombies {encoding, contents, ..} => *encoding = get_encoding(contents),_ => (),};hunk}#[cfg(test)]impl Arbitrary for PrintablePerms {fn arbitrary(g: &mut Gen) -> Self {*g.choose(&[PrintablePerms::IsDir,PrintablePerms::IsExecutable,PrintablePerms::IsFile,]).unwrap()}}#[cfg(test)]impl Arbitrary for PrintablePos {fn arbitrary(g: &mut Gen) -> Self {PrintablePos(usize::arbitrary(g), u64::arbitrary(g))}fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {Box::new(self.0.shrink().zip(self.1.shrink()).map(|(a, b)| PrintablePos(a, b)),)}}#[cfg(test)]impl Arbitrary for PrintableEdge {fn arbitrary(g: &mut Gen) -> Self {Self {previous: Arbitrary::arbitrary(g),flag: Arbitrary::arbitrary(g),from: Arbitrary::arbitrary(g),to_start: Arbitrary::arbitrary(g),to_end: Arbitrary::arbitrary(g),introduced_by: Arbitrary::arbitrary(g),}}fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {let Self {previous,flag,from,to_start,to_end,introduced_by,} = self.clone();Box::new((previous, flag, from, to_start, to_end, introduced_by).shrink().map(|(previous, flag, from, to_start, to_end, introduced_by)| Self {previous,flag,from,to_start,to_end,introduced_by,},),)}}#[cfg(test)]impl Arbitrary for PrintableNewVertex {fn arbitrary(g: &mut Gen) -> Self {Self {up_context: Arbitrary::arbitrary(g),start: Arbitrary::arbitrary(g),end: Arbitrary::arbitrary(g),down_context: Arbitrary::arbitrary(g),}}fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {let Self {up_context,start,end,down_context,} = self.clone();Box::new((up_context, start, end, down_context).shrink().map(|(up_context, start, end, down_context)| Self {up_context,start,end,down_context,},))}}#[cfg(test)]impl Arbitrary for PrintableAtom {fn arbitrary(g: &mut Gen) -> Self {Gen::new(g.size()).choose(&[PrintableAtom::NewVertex(Arbitrary::arbitrary(g)),PrintableAtom::Edges(Arbitrary::arbitrary(g)),]).unwrap().clone()}fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {match self {PrintableAtom::NewVertex(x) => {Box::new(x.shrink().map(|x| PrintableAtom::NewVertex(x)))}PrintableAtom::Edges(x) => Box::new(x.shrink().map(|x| PrintableAtom::Edges(x))),}}}#[cfg(test)]impl Arbitrary for PrintableEdgeFlags {fn arbitrary(g: &mut Gen) -> Self {Self {block: Arbitrary::arbitrary(g),folder: Arbitrary::arbitrary(g),deleted: Arbitrary::arbitrary(g),}}fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {Box::new((self.block, self.folder, self.deleted).shrink().map(|(block, folder, deleted)| Self {block,folder,deleted,},))}} - file addition: parse.rs[49.931000]
use nom::branch::alt;use nom::bytes::complete::*;use nom::character::complete::*;use nom::combinator::*;use nom::error::ParseError;use nom::multi::*;use nom::sequence::*;use nom::*;use crate::change::printable::*;use PrintableHunk::*;use super::*;fn parse_file_move_v_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Moved:"), space0), parse_string)(i)?;let (i, name) = preceded(space0, parse_string)(i)?;let (i, perms) = preceded(space0, parse_perms)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, del) = parse_edges(i)?;let (i, up_context) = preceded(pair(space0, tag("up")), parse_context)(i)?;let (i, down_context) = preceded(pair(space0, tag(", down")), parse_context)(i)?;let (i, _) = newline(i)?;Ok((i,FileMoveV {path,name,perms,pos,up_context,down_context,del,},))}fn parse_file_move_e_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Moved:"), space0), parse_string)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, add) = parse_edges(i)?;let (i, del) = parse_edges(i)?;Ok((i,FileMoveE {path,pos,add,del,},))}fn parse_file_del_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("File deletion:"), space0),parse_string,)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, del_edges) = parse_edges(i)?;let (i, content_edges) = parse_edges(i)?;let (i, contents) = parse_contents('-', encoding.clone(), i)?;Ok((i,FileDel {path,pos,encoding,del_edges,content_edges,contents,},))}fn parse_file_undel_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("File un-deletion:"), space0),parse_string,)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, undel_edges) = parse_edges(i)?;let (i, content_edges) = parse_edges(i)?;let (i, contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,FileUndel {path,pos,encoding,undel_edges,content_edges,contents,},))}fn parse_file_addition_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, name) = preceded(delimited(space0, tag("File addition:"), space0),parse_string,)(i)?;let (i, parent) = preceded(delimited(space1, tag("in"), space1), parse_string)(i)?;let (i, perms) = preceded(space0, parse_perms)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline, multispace0))(i)?;let (i, up_context) = preceded(tag("up"), parse_context)(i)?;let (i, (start, end)) = delimited(space0, parse_start_end, pair(space0, newline))(i)?;let (i, contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,FileAddition {name,parent,perms,encoding,up_context,start,end,contents,},))}/// Parse a hunk header stringfn parse_edit_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Edit in"), space0), parse_string)(i)?;let (i, line) = preceded(char(':'), u64)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, change) = parse_atom(i)?;let (i, contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,Edit {path,line: line as usize,pos,encoding,change,contents,},))}fn parse_replace_hunk(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Replacement in"), space0),parse_string,)(i)?;let (i, line) = preceded(char(':'), u64)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;// TODO: allow newlines in between these lineslet (i, change) = parse_edges(i)?;let (i, replacement) = parse_new_vertex(i)?;let (i, change_contents) = parse_contents('-', encoding.clone(), i)?;let (i, replacement_contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,Replace {path,line: line as usize,pos,encoding,change,replacement,change_contents,replacement_contents,},))}fn parse_solve_name_conflict(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Solving a name conflict in"), space0),parse_string,)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, _) = tuple((space0, char(':'), space0))(i)?;let (i, names) = separated_list0(tuple((space0, char(','), space0)), parse_string)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, edges) = parse_edges(i)?;Ok((i,SolveNameConflict {path,pos,names,edges,},))}fn parse_unsolve_name_conflict(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Un-solving a name conflict in"), space0),parse_string,)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, _) = tuple((space0, char(':'), space0))(i)?;let (i, names) = separated_list0(tuple((space0, char(','), space0)), parse_string)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, edges) = parse_edges(i)?;Ok((i,UnsolveNameConflict {path,pos,names,edges,},))}fn parse_solve_order_conflict(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Solving an order conflict in"), space0),parse_string,)(i)?;let (i, line) = preceded(char(':'), u64)(i)?;let (i, pos) = preceded(space1, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, change) = parse_new_vertex(i)?;let (i, contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,SolveOrderConflict {path,line: line as usize,pos,encoding,change,contents,},))}fn parse_unsolve_order_conflict(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Un-solving an order conflict in"), space0),parse_string,)(i)?;let (i, line) = preceded(char(':'), u64)(i)?;let (i, pos) = preceded(space1, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, change) = parse_edges(i)?;let (i, contents) = parse_contents('-', encoding.clone(), i)?;Ok((i,UnsolveOrderConflict {path,line: line as usize,pos,encoding,change,contents,},))}fn parse_resurrect_zombies(i: &str) -> IResult<&str, PrintableHunk> {let (i, path) = preceded(delimited(space0, tag("Resurrecting zombie lines in"), space0),parse_string,)(i)?;let (i, line) = preceded(char(':'), u64)(i)?;let (i, pos) = preceded(space0, parse_printable_pos)(i)?;let (i, encoding) = preceded(space0, parse_encoding)(i)?;let (i, _) = tuple((space0, newline))(i)?;let (i, change) = parse_edges(i)?;let (i, contents) = parse_contents('+', encoding.clone(), i)?;Ok((i,ResurrectZombies {path,line: line as usize,pos,encoding,change,contents,},))}fn parse_content_line(leading_char: char, input: &str) -> IResult<&str, String> {preceded(char(leading_char),alt((map(delimited(tag(" "), take_till(|c| c == '\n'), newline),|s: &str| s.to_string() + "\n",),map(delimited(tag("b"), take_till(|c| c == '\n'), newline),|s: &str| s.to_string(),),)),)(input)}// TODO: better error handlingfn parse_contents(leading_char: char,encoding: Option<Encoding>,i: &str,) -> IResult<&str, Vec<u8>> {let (i, res) = fold_many0(complete(|i| parse_content_line(leading_char, i)),String::new,|s, r| s + r.as_str(),)(i)?;let (i, backslash) = opt(complete(tag("\\\n")))(i)?;if let Ok(mut vec) = encode(encoding, &res) {if backslash.is_some() && vec[vec.len() - 1] == b'\n' {vec.pop();}Ok((i, vec))} else {Err(nom::Err::Error(nom::error::Error::new(i,nom::error::ErrorKind::Verify,)))}}fn encode(encoding: Option<Encoding>, contents: &str) -> Result<Vec<u8>, String> {if let Some(encoding) = encoding {Ok(encoding.encode(contents).to_vec())} else {data_encoding::BASE64.decode(contents.as_bytes()).map_err(|e| e.to_string())}}fn parse_encoding(input: &str) -> IResult<&str, Option<Encoding>> {map(parse_string, |e| {if e != BINARY_LABEL {Some(Encoding::for_label(&e))} else {None}})(input)}pub fn parse_numbered_hunk(input: &str) -> IResult<&str, (u64, PrintableHunk)> {tuple((terminated(u64, char('.')), parse_hunk))(input)}pub fn parse_hunk(input: &str) -> IResult<&str, PrintableHunk> {alt((parse_file_move_v_hunk,parse_file_move_e_hunk,parse_file_del_hunk,parse_file_undel_hunk,parse_file_addition_hunk,parse_edit_hunk,parse_replace_hunk,parse_solve_name_conflict,parse_unsolve_name_conflict,parse_solve_order_conflict,parse_unsolve_order_conflict,parse_resurrect_zombies,))(input)}pub fn parse_hunks(input: &str) -> IResult<&str, Vec<(u64, PrintableHunk)>> {preceded(tuple((tag("# Hunks"), space0, newline, multispace0)),many0(complete(terminated(parse_numbered_hunk, multispace0))),)(input)}pub const BINARY_LABEL: &str = "binary";pub fn encoding_label(encoding: &Option<Encoding>) -> &str {match encoding {Some(encoding) => encoding.label(),_ => BINARY_LABEL,}}/// Parse an escaped character: \n, \t, \r, etc.fn parse_escaped_char(input: &str) -> IResult<&str, char> {preceded(char('\\'),alt((value('\n', char('n')),value('\r', char('r')),value('\t', char('t')),value('\u{08}', char('b')),value('\u{0C}', char('f')),value('\\', char('\\')),value('"', char('"')),)),)(input)}/// Parse a non-empty block of text that doesn't include \ or "fn parse_literal<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, &'a str, E> {let not_quote_slash = is_not("\"\\");verify(not_quote_slash, |s: &str| !s.is_empty())(input)}/// Combine parse_literal and parse_escaped_char into a StringFragment.fn parse_fragment(input: &str) -> IResult<&str, StringFragment> {alt((map(parse_literal, StringFragment::Literal),map(parse_escaped_char, StringFragment::EscapedChar),))(input)}/// Parse a string. Use a loop of parse_fragment and push all of the fragments/// into an output string.pub fn parse_string(input: &str) -> IResult<&str, String> {let build_string = fold_many0(parse_fragment, String::new, |mut string, fragment| {match fragment {StringFragment::Literal(s) => string.push_str(s),StringFragment::EscapedChar(c) => string.push(c),}string});delimited(char('"'), build_string, char('"'))(input)}fn parse_perms(input: &str) -> IResult<&str, PrintablePerms> {alt((value(PrintablePerms::IsDir, tag("+dx")),value(PrintablePerms::IsExecutable, tag("+x")),value(PrintablePerms::IsFile, tag("")),))(input)}fn parse_printable_pos(input: &str) -> IResult<&str, PrintablePos> {map(separated_pair(u64, char('.'), u64), |(a, b)| {PrintablePos(a as usize, b)})(input)}fn parse_context(input: &str) -> IResult<&str, Vec<PrintablePos>> {delimited(space0, separated_list0(space1, parse_printable_pos), space0)(input)}fn parse_start_end(input: &str) -> IResult<&str, (u64, u64)> {preceded(pair(tag(", new"), space1),separated_pair(u64, char(':'), u64),)(input)}fn parse_edge_flags(i: &str) -> IResult<&str, PrintableEdgeFlags> {let (i, block) = map(opt(char('B')), |x| x.is_some())(i)?;let (i, folder) = map(opt(char('F')), |x| x.is_some())(i)?;let (i, deleted) = map(opt(char('D')), |x| x.is_some())(i)?;Ok((i,PrintableEdgeFlags {block,folder,deleted,},))}fn parse_new_vertex(i: &str) -> IResult<&str, PrintableNewVertex> {map(tuple((space0,preceded(tag("up"), parse_context),terminated(tag(", new"), space0),terminated(u64, char(':')),terminated(u64, space0),preceded(tag(", down"), parse_context),newline,)),|(_, up_context, _, start, end, down_context, _)| PrintableNewVertex {up_context,start,end,down_context,},)(i)}fn parse_edge(i: &str) -> IResult<&str, PrintableEdge> {map(tuple((terminated(parse_edge_flags, char(':')),terminated(parse_edge_flags, char(' ')),terminated(parse_printable_pos, tag(" -> ")),terminated(parse_printable_pos, tag(":")),terminated(u64, tag("/")),terminated(u64, space0),)),|(previous, flag, from, to_start, to_end, introduced_by)| PrintableEdge {previous,flag,from,to_start,to_end,introduced_by: introduced_by as usize,},)(i)}fn parse_atom(i: &str) -> IResult<&str, PrintableAtom> {alt((map(parse_new_vertex, PrintableAtom::NewVertex),map(parse_edges, PrintableAtom::Edges),))(i)}fn parse_edges(input: &str) -> IResult<&str, Vec<PrintableEdge>> {terminated(separated_list0(delimited(space0, char(','), space0), complete(parse_edge)),pair(space0, newline),)(input)}pub fn parse_header(input: &str) -> IResult<&str, Result<ChangeHeader, toml::de::Error>> {map(alt((take_until("# Dependencies"), take_until("# Hunks"))),|s| toml::de::from_str(s),)(input)}pub fn parse_dependency(i: &str) -> IResult<&str, PrintableDep> {let (i, mut type_) = delimited(char('['),alt((map(u64, |n| DepType::Numbered(n as usize, false)),value(DepType::ExtraKnown, char('*')),value(DepType::ExtraUnknown, take_till(|c| c != ']')),)),char(']'),)(i)?;let (i, plus) = terminated(alt((value(true, char('+')), value(false, char(' ')))),space0,)(i)?;// TODO: get rid of this confusing mutationtype_ = if let DepType::Numbered(n, _) = type_ {DepType::Numbered(n, plus)} else {type_};let (i, hash) = delimited(space0,take_while(|c: char| c.is_ascii_alphanumeric()),pair(space0, char('\n')),)(i)?;Ok((i,PrintableDep {type_,hash: hash.to_string(),},))}pub fn parse_dependencies(input: &str) -> IResult<&str, Vec<PrintableDep>> {alt((preceded(tuple((tag("# Dependencies"), space0, char('\n'), multispace0)),many0(terminated(parse_dependency, multispace0)),),value(Vec::new(), multispace0),))(input)} - edit in libpijul/Cargo.toml at line 118
nom = "7" - edit in libpijul/Cargo.toml at line 154[4.3715]
quickcheck = "1"quickcheck_macros = "1" - edit in Cargo.lock at line 1093
"nom", - edit in Cargo.lock at line 1098[54.23364][4.3964]
"quickcheck","quickcheck_macros", - edit in Cargo.lock at line 1233[54.26734][54.26734]
name = "minimal-lexical"version = "0.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d"[[package]] - edit in Cargo.lock at line 1302[54.28286][54.28286]
name = "nom"version = "7.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1"dependencies = ["memchr","minimal-lexical","version_check",][[package]] - edit in Cargo.lock at line 1626[54.36174][54.36174]
][[package]]name = "quickcheck"version = "1.0.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"dependencies = ["env_logger","log","rand 0.8.4", - edit in Cargo.lock at line 1640[54.36189][54.36189]
name = "quickcheck_macros"version = "1.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"dependencies = ["proc-macro2","quote","syn",][[package]]