Complete the end-to-end CLI parity audit
This commit is contained in:
1 parent
4bd39b1ef2
commit
0a905e5e14
14 files changed
+1476
-95
No files matched your search
@@ -24,7 +24,7 @@ use crate::{
|
||||
crypto::{KeyHandle, KeyStore, SecretProvider},
|
||||
mutation::{TreeCommit, TreeCommitError, TreeCommitter},
|
||||
recipient::{PolicyCommit, PolicyCommitError, PolicyCommitter},
|
||||
repository::Repository,
|
||||
repository::{EncryptedEntry, Repository, SecretBytes},
|
||||
write::{EntryCommit, EntryCommitError, EntryCommitter},
|
||||
};
|
||||
|
||||
@@ -1587,6 +1587,41 @@ impl GitRepository {
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Render a helper-free working-tree diff. Password entries are decrypted
|
||||
/// in storage before rendering, so ciphertext is never used as display
|
||||
/// state and no Git textconv or GPG process is required.
|
||||
pub fn render_diff(
|
||||
&self,
|
||||
paths: &[PathBuf],
|
||||
keys: &KeyStore,
|
||||
secrets: &mut impl SecretProvider,
|
||||
) -> Result<SecretBytes, GitError> {
|
||||
let mut rendered = Vec::new();
|
||||
for entry in self.diff(paths)? {
|
||||
let display = entry.path().to_string_lossy();
|
||||
rendered.extend_from_slice(
|
||||
format!("diff --ironstorage a/{display} b/{display}\n").as_bytes(),
|
||||
);
|
||||
match entry.old() {
|
||||
Some(_) => rendered.extend_from_slice(format!("--- a/{display}\n").as_bytes()),
|
||||
None => rendered.extend_from_slice(b"--- /dev/null\n"),
|
||||
}
|
||||
match entry.current() {
|
||||
Some(_) => rendered.extend_from_slice(format!("+++ b/{display}\n").as_bytes()),
|
||||
None => rendered.extend_from_slice(b"+++ /dev/null\n"),
|
||||
}
|
||||
if let Some(old) = entry.old() {
|
||||
let plaintext = diff_contents(entry.path(), old, keys, secrets)?;
|
||||
append_diff_lines(&mut rendered, b'-', plaintext.expose());
|
||||
}
|
||||
if let Some(current) = entry.current() {
|
||||
let plaintext = diff_contents(entry.path(), current, keys, secrets)?;
|
||||
append_diff_lines(&mut rendered, b'+', plaintext.expose());
|
||||
}
|
||||
}
|
||||
Ok(SecretBytes::new(rendered))
|
||||
}
|
||||
|
||||
fn head_tree_map(&self) -> Result<Option<BTreeMap<String, gix::hash::ObjectId>>, GitError> {
|
||||
let Some(id) = self.repository.head_id().ok() else {
|
||||
return Ok(None);
|
||||
@@ -1639,6 +1674,34 @@ impl GitRepository {
|
||||
}
|
||||
}
|
||||
|
||||
fn diff_contents(
|
||||
path: &Path,
|
||||
contents: &[u8],
|
||||
keys: &KeyStore,
|
||||
secrets: &mut impl SecretProvider,
|
||||
) -> Result<SecretBytes, GitError> {
|
||||
if path.extension().is_some_and(|extension| extension == "gpg") {
|
||||
keys.decrypt(&EncryptedEntry::new(contents.to_vec()), secrets)
|
||||
.map_err(invalid)
|
||||
} else {
|
||||
Ok(SecretBytes::new(contents.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
fn append_diff_lines(output: &mut Vec<u8>, prefix: u8, contents: &[u8]) {
|
||||
if contents.is_empty() {
|
||||
return;
|
||||
}
|
||||
for line in contents.split_inclusive(|byte| *byte == b'\n') {
|
||||
output.push(prefix);
|
||||
output.extend_from_slice(line);
|
||||
if !line.ends_with(b"\n") {
|
||||
output.push(b'\n');
|
||||
output.extend_from_slice(b"\\ No newline at end of file\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EntryCommitter for GitRepository {
|
||||
fn commit(&mut self, change: &EntryCommit) -> Result<(), EntryCommitError> {
|
||||
self.stage_and_commit(&[change.path().encrypted_relative_path()], change.message())
|
||||
|
||||
Reference in new issue
Block a user