Proper escaping of UTF-8 filenames in the patch text format

pmeunier
Sep 7, 2021, 11:59 AM
SFJ3XRTFUNG6KNYDLIYKHCENZ6Y3PG33GNGDW6444LAVBMCSH2FAC

Dependencies

  • [2] NE4A4WUK Parsing of file addition metadata
  • [3] FXEDPLRI Resurrecting tests, and type cleanup (no need for Arc<RwLock<…>> anymore)
  • [4] ZSF3YFZT encoded file deletion
  • [5] RRCSHAYZ Formatting
  • [6] KWAGWB73 Adding extra dependencies from the config file
  • [7] CIEUBH46 Fixing an index-out-of-bounds error when serialising bad changes
  • [8] L4JXJHWX pijul/*: reorganize imports and remove extern crate
  • [9] 5BB266P6 Optional colours in the global config file
  • [10] Q4SVMHAE Removing --channel from the changes command
  • [11] I24UEJQL Various post-fire fixes
  • [12] XSEODPNE Fixing conflicts
  • [13] VYHHOEYH Versions and formatting
  • [14] SXEYMYF7 Fixing the bad changes in history (unfortunately, by rebooting).
  • [15] YN63NUZO Sanakirja 1.0
  • [16] XR7MNOMU file encoding in updates
  • [17] 3S6LU2U5 abstract out FileMetadata (de)serialistion
  • [18] RUBSM5DR Fixing a bug when outputting changes in text format
  • [19] CCLLB7OI Upgrading to Sanakirja 0.15 + version bump
  • [20] VO5OQW4W Removing anyhow in libpijul
  • [21] NYOF5766 track file encoding in the record, including change text for file adds
  • [22] CCFJ7VO3 Renaming "Record" to "Hunk" in the changes
  • [23] ZRUPLBBT Colours in diff and change: separating concerns and dependencies
  • [24] X243Z3Y5 Recording only the required metadata (can even be changed later!)

Change contents

  • edit in ""pijul/src/commands/record.rs"" at line 363
    [4.107873][4.107873:107972]()
    let file_name = |local: &Local, _| -> String { format!("{}:{}", local.path, local.line) };
  • replacement in ""pijul/src/commands/record.rs"" at line 371
    [4.462][4.108241:108308](),[4.108241][4.108241:108308]()
    change.write(changes, None, file_name, true, &mut o)?;
    [4.462]
    [4.463]
    change.write(changes, None, true, &mut o)?;
  • edit in ""pijul/src/commands/diff.rs"" at line 231
    [4.177116][4.177116:177261]()
    |local: &libpijul::change::Local, _| -> String {
    format!("{}:{}", local.path, local.line)
    },
  • edit in ""pijul/src/commands/change.rs"" at line 4
    [4.3123][4.189333:189362](),[4.189333][4.189333:189362]()
    use libpijul::change::Local;
  • edit in ""pijul/src/commands/change.rs"" at line 42
    [4.190342][4.346:419]()
    let file_name = |l: &Local, _| format!("{}:{}", l.path, l.line);
  • edit in ""pijul/src/commands/change.rs"" at line 46
    [4.2218][4.2218:2241]()
    file_name,
  • edit in ""libpijul/src/change/text_changes.rs"" at line 91
    [4.1465][4.1465:1525]()
    F: FnMut(&Local, Position<Option<Hash>>) -> String,
  • edit in ""libpijul/src/change/text_changes.rs"" at line 95
    [4.40762][4.40762:40788]()
    mut file_name: F,
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 153
    [4.42756][4.42756:42841]()
    rec.write(changes, &mut file_name, &hashes, &self.contents, &mut w)?
    [4.42756]
    [4.42841]
    rec.write(changes, &hashes, &self.contents, &mut w)?
  • edit in ""libpijul/src/change/text_changes.rs"" at line 309
    [4.3177]
    [4.48443]
    struct Escaped<'a>(&'a str);
    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(())
    }
    }
    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()
    }
    }
  • edit in ""libpijul/src/change/text_changes.rs"" at line 357
    [4.48544][4.48544:48604]()
    F: FnMut(&Local, Position<Option<Hash>>) -> String,
  • edit in ""libpijul/src/change/text_changes.rs"" at line 360
    [4.48647][4.48647:48673]()
    mut file_name: F,
  • edit in ""libpijul/src/change/text_changes.rs"" at line 364
    [4.48808]
    [4.48808]
    // let file_name = |local: &Local, _| -> String { format!("{}:{}", local.path, local.line) };
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 378
    [4.1537][4.1537:1644]()
    "Moved: {:?} {:?} {}",
    path,
    name,
    [4.1537]
    [4.1644]
    "Moved: {} {} {}",
    Escaped(&path),
    Escaped(&name),
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 420
    [4.269][3.105657:105716]()
    write!(w, "File deletion: {:?} ", path,)?;
    [4.269]
    [4.50578]
    write!(w, "File deletion: {} ", Escaped(path))?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 440
    [4.308][3.105717:105779]()
    write!(w, "File un-deletion: {:?} ", path,)?;
    [4.308]
    [4.51227]
    write!(w, "File un-deletion: {} ", Escaped(path))?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 476
    [4.52618][4.97:165](),[4.165][4.2008:2070](),[4.2008][4.2008:2070]()
    "File addition: {:?} in {:?}{} {:?}\n up",
    name,
    parent,
    [4.52618]
    [4.2070]
    "File addition: {} in {}{} \"{}\"\n up",
    Escaped(name),
    Escaped(parent),
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 509
    [4.341][4.53458:53536](),[4.53458][4.53458:53536]()
    write!(w, "Edit in {} ", file_name(&local, change.inode()))?;
    [4.341]
    [4.53536]
    write!(w, "Edit in \"{}\":{} ", Escaped(&local.path), local.line)?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 523
    [4.381][4.53905:53990](),[4.53905][4.53905:53990]()
    write!(w, "Replacement in {} ", file_name(&local, change.inode()))?;
    [4.381]
    [4.53990]
    write!(w, "Replacement in \"{}\":{} ", Escaped(&local.path), local.line)?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 533
    [4.9011][4.54434:54504](),[4.54434][4.54434:54504]()
    write!(w, "Solving a name conflict in {:?} ", path)?;
    [4.9011]
    [4.54504]
    write!(w, "Solving a name conflict in {} ", Escaped(path))?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 541
    [4.9070][4.54813:54886](),[4.54813][4.54813:54886]()
    write!(w, "Un-solving a name conflict in {:?} ", path)?;
    [4.9070]
    [4.54886]
    write!(w, "Un-solving a name conflict in {} ", Escaped(path))?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 552
    [4.55244][4.55244:55354]()
    "Solving an order conflict in {} ",
    file_name(&local, change.inode())
    [4.55244]
    [4.55354]
    "Solving an order conflict in \"{}\":{} ",
    Escaped(&local.path),
    local.line,
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 565
    [4.55725][4.55725:55838]()
    "Un-solving an order conflict in {} ",
    file_name(&local, change.inode())
    [4.55725]
    [4.55838]
    "Un-solving an order conflict in \"{}\":{} ",
    Escaped(&local.path),
    local.line,
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 582
    [4.56205][4.56205:56309]()
    "Resurrecting zombie lines in {:?}:{} ",
    local.path, local.line
    [4.56205]
    [4.56309]
    "Resurrecting zombie lines in {}:{} ",
    Escaped(&local.path), local.line
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 586
    [4.56389][4.3532:3595]()
    write!(w, " {:?}", encoding_label(encoding))?;
    [4.56389]
    [4.56389]
    write!(w, " \"{}\"", encoding_label(encoding))?;
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 651
    [4.59079][4.59079:59139]()
    let name = &cap.name("name").unwrap().as_str();
    [4.59079]
    [4.59139]
    let name = unescape(&cap.name("name").unwrap().as_str());
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 657
    [4.59326][2.0:45](),[2.45][4.59365:59391](),[4.59365][4.59365:59391]()
    parent.to_string() + "/"
    }) + name
    [4.59326]
    [4.59391]
    unescape(&parent).to_string() + "/"
    }) + &name
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 676
    [4.3129][4.3129:3161]()
    basename: name,
    [4.3129]
    [4.5888]
    basename: &name,
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 806
    [4.64287][4.64287:64345]()
    let name = cap.name("new").unwrap().as_str();
    [4.64287]
    [4.2937]
    let name = unescape(cap.name("new").unwrap().as_str());
  • replacement in ""libpijul/src/change/text_changes.rs"" at line 821
    [4.3302][4.3302:3334]()
    basename: name,
    [4.3302]
    [4.6078]
    basename: &name,