diff options
author | tummychow <tummychow@users.noreply.github.com> | 2018-03-10 22:31:18 -0800 |
---|---|---|
committer | tummychow <tummychow@users.noreply.github.com> | 2018-03-10 22:31:40 -0800 |
commit | dc1612aec44cc9287ec49e038e20abdeedf2737f (patch) | |
tree | aa6761af4bb354b8b5f2b94e0b385b326bb1ebc0 | |
parent | 3c44cbfe2582076685caea13d81e7191251f9e0e (diff) |
assemble tree with a hunk applied
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/commute.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 59 |
4 files changed, 71 insertions, 1 deletions
@@ -139,6 +139,7 @@ dependencies = [ "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "slog-async 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -242,6 +243,14 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "memchr" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "num" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -572,6 +581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" "checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" +"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" @@ -10,6 +10,7 @@ slog = "~2.1" slog-term = "~2.3" slog-async = "~2.2" failure = "~0.1" +memchr = "~2.0" [dev-dependencies] tempdir = "~0.3" diff --git a/src/commute.rs b/src/commute.rs index 03dfefd..e4969ce 100644 --- a/src/commute.rs +++ b/src/commute.rs @@ -12,7 +12,7 @@ use owned; /// - the first unchanged line after it, on the added side /// /// This function returns those four line numbers, in that order. -fn anchors(hunk: &owned::Hunk) -> (usize, usize, usize, usize) { +pub fn anchors(hunk: &owned::Hunk) -> (usize, usize, usize, usize) { match (hunk.removed.lines.len(), hunk.added.lines.len()) { (0, 0) => (0, 1, 0, 1), (removed_len, 0) => ( @@ -1,5 +1,6 @@ extern crate failure; extern crate git2; +extern crate memchr; #[macro_use] extern crate slog; @@ -7,6 +8,8 @@ mod owned; mod stack; mod commute; +use std::io::Write; + pub struct Config<'a> { pub dry_run: bool, pub force: bool, @@ -147,3 +150,59 @@ pub fn run(config: &Config) -> Result<(), failure::Error> { Ok(()) } + +fn apply_hunk_to_tree<'repo>( + repo: &'repo git2::Repository, + base: &git2::Tree, + hunk: &owned::Hunk, + path: &[u8], +) -> Result<git2::Tree<'repo>, failure::Error> { + let mut treebuilder = repo.treebuilder(Some(base))?; + let (blob, mode) = { + let entry = treebuilder + .get(path)? + .ok_or_else(|| failure::err_msg("couldn't find tree entry for path"))?; + (repo.find_blob(entry.id())?, entry.filemode()) + }; + + // TODO: convert path to OsStr and pass it during blob_writer + // creation, to get gitattributes handling (note that converting + // &[u8] to &std::path::Path is only possible on unixy platforms) + let mut blobwriter = repo.blob_writer(None)?; + let old_content = blob.content(); + let (old_start, _, _, _) = commute::anchors(hunk); + + // first, write the lines from the old content that are above the + // hunk + let old_content = { + let (pre, post) = old_content.split_at(skip_past_nth(b'\n', old_content, old_start)); + blobwriter.write_all(pre)?; + post + }; + // next, write the added side of the hunk + for line in &**hunk.added.lines { + blobwriter.write_all(line)?; + } + // if this hunk removed lines from the old content, those must be + // skipped + let old_content = &old_content[skip_past_nth(b'\n', old_content, hunk.removed.lines.len())..]; + // finally, write the remaining lines of the old content + blobwriter.write_all(old_content)?; + + treebuilder.insert(path, blobwriter.commit()?, mode)?; + Ok(repo.find_tree(treebuilder.write()?)?) +} + +fn skip_past_nth(needle: u8, haystack: &[u8], n: usize) -> usize { + if n == 0 { + return 0; + } + + // TODO: is fuse necessary here? + memchr::Memchr::new(needle, haystack) + .fuse() + .skip(n - 1) + .next() + .map(|x| x + 1) + .unwrap_or(haystack.len()) +} |