summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortummychow <tummychow@users.noreply.github.com>2018-03-10 22:31:18 -0800
committertummychow <tummychow@users.noreply.github.com>2018-03-10 22:31:40 -0800
commitdc1612aec44cc9287ec49e038e20abdeedf2737f (patch)
treeaa6761af4bb354b8b5f2b94e0b385b326bb1ebc0
parent3c44cbfe2582076685caea13d81e7191251f9e0e (diff)
assemble tree with a hunk applied
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--src/commute.rs2
-rw-r--r--src/lib.rs59
4 files changed, 71 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bcde62a..871b651 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index aaee08a..07bc8cb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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) => (
diff --git a/src/lib.rs b/src/lib.rs
index 2366054..0a9dd99 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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())
+}