Repository: o0Ignition0o/cargo-scout (language: rust)

Issue:
Consider using nom ?
As an overall refactoring effort, using nom might ease git diff parsing (and help us towards #41)

[Nom is really cool to use since 5.0](https://github.com/Geal/nom), maybe we could try to use it!



Discussion / hints:
Have you considered using [libgit2](https://github.com/rust-lang/git2-rs)? It should be possible to gather [all](https://docs.rs/git2/0.10.2/git2/struct.Diff.html) [the](https://docs.rs/git2/0.10.2/git2/struct.DiffDelta.html) [necessary](https://docs.rs/git2/0.10.2/git2/struct.DiffHunk.html) information programmatically and parsing could be avoided completely. 

The code should be cleaner and it should be more robust as we would not rely on git diff output which could change in the future. As a bonus you avoid spawning an external process and the git binary dependency altogether.

If you like the idea I could give it a try :)
That's a great idea !
I dismissed it at first because I didn't want cargo-scout to become too huge, and it was just a POC, but I think a good libgit2 implementation would be _very_ useful !

If it cargo-scout works out well we may someday consider using [cargo](https://docs.rs/cargo/0.40.0/cargo/) as a library as well :D

Libgit2 is definitely something worth playing with If you would like to !

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index eeddf6e..99546d0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22,14 +22,24 @@ name = "bitflags"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "c2-chacha"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "cargo-scout"
 version = "0.2.0"
 dependencies = [
  "cargo_toml 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -42,6 +52,20 @@ dependencies = [
  "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "cc"
+version = "1.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "clap"
 version = "2.33.0"
@@ -56,6 +80,28 @@ dependencies = [
  "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "git2"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libgit2-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "heck"
 version = "0.3.1"
@@ -64,16 +110,103 @@ dependencies = [
  "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "hermit-abi"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "idna"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "itoa"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "jobserver"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "libc"
 version = "0.2.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "libgit2-sys"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "proc-macro-error"
 version = "0.2.6"
@@ -100,6 +233,56 @@ dependencies = [
  "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "rand"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.2"
@@ -133,6 +316,11 @@ dependencies = [
  "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "smallvec"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "strsim"
 version = "0.8.0"
@@ -169,6 +357,19 @@ dependencies = [
  "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "textwrap"
 version = "0.11.0"
@@ -185,6 +386,22 @@ dependencies = [
  "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "unicode-bidi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "unicode-segmentation"
 version = "1.6.0"
@@ -200,11 +417,31 @@ name = "unicode-xid"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "url"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "vec_map"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "wasi"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "winapi"
 version = "0.3.8"
@@ -228,28 +465,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
 "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
 "checksum cargo_toml 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7877b00aaf997d7ed66a81281d3a8b9f9da5361df05b72785b985349979a0f3"
+"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
+"checksum git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c1af51ea8a906616af45a4ce78eacf25860f7a13ae7bf8a814693f0f4037a26"
 "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
+"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
 "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
+"checksum jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b1d42ef453b30b7387e113da1c83ab1605d90c5b4e0eb8e96d016ed3b8c160"
 "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
+"checksum libgit2-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4870c781f6063efb83150cd22c1ddf6ecf58531419e7570cdcced46970f64a16"
+"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
+"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
+"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
 "checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097"
 "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
 "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
+"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
+"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
+"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
 "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
 "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702"
 "checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0"
 "checksum serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "1a3351dcbc1f067e2c92ab7c3c1f288ad1a4cffc470b5aaddb4c2e0a3ae80043"
+"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86"
 "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 "checksum structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3a3e93f5ad553c38b3301c8a0a0cec829a36783f6a0c467fc4bf553a5f5bf"
 "checksum structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e"
 "checksum syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f89693ae015201f8de93fd96bde2d065f8bfc3f97ce006d5bc9f900b97c0c7c0"
+"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
 "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
 "checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
+"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf"
 "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
 "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
 "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
+"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
+"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
 "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index 582e509..3d642c1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,7 @@ serde = { version = "1.0.103", features = ["derive"] }
 serde_json = "1.0.42"
 structopt = "0.3.5"
 cargo_toml = "0.8.0"
+git2 = { version = "0.10.2", default-features = false }
+
+[dev-dependencies]
+tempfile = "3.1.0"
diff --git a/src/clippy.rs b/src/clippy.rs
index 7cd38ab..71b1a4c 100644
--- a/src/clippy.rs
+++ b/src/clippy.rs
@@ -40,8 +40,8 @@ pub struct Message {
 #[derive(Deserialize, PartialEq, Debug, Clone)]
 pub struct Span {
     pub file_name: String,
-    pub line_start: i32,
-    pub line_end: i32,
+    pub line_start: u32,
+    pub line_end: u32,
 }
 
 pub struct Linter {
diff --git a/src/error.rs b/src/error.rs
index 315e096..5aa7475 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,6 +6,7 @@ pub enum Error {
     Json(serde_json::Error),
     NotClean,
     Io(std::io::Error),
+    Git(git2::Error),
 }
 
 impl From<std::string::FromUtf8Error> for Error {
@@ -37,3 +38,9 @@ impl From<cargo_toml::Error> for Error {
         Self::CargoToml(err)
     }
 }
+
+impl From<git2::Error> for Error {
+    fn from(err: git2::Error) -> Self {
+        Self::Git(err)
+    }
+}
diff --git a/src/git.rs b/src/git.rs
index 0527db9..79ae7dd 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -1,263 +1,250 @@
-use std::process::Command;
-
-pub struct Parser {
-    verbose: bool,
-}
+use crate::error::Error;
+use git2::{Delta, DiffOptions, Repository};
+use std::path::Path;
 
 #[derive(Debug, PartialEq)]
 pub struct Section {
     pub file_name: String,
-    pub line_start: i32,
-    pub line_end: i32,
-}
-
-#[derive(Debug)]
-pub struct SectionBuilder {
-    file_name: Option<String>,
-    line_start: Option<i32>,
-    line_end: Option<i32>,
-}
-
-impl SectionBuilder {
-    pub fn new() -> Self {
-        Self {
-            file_name: None,
-            line_start: None,
-            line_end: None,
-        }
-    }
-
-    pub fn file_name(&mut self, file_name: String) {
-        self.file_name = Some(file_name);
-    }
-
-    pub fn line_start(&mut self, line_start: i32) {
-        self.line_start = Some(line_start);
-    }
-
-    pub fn line_end(&mut self, line_end: i32) {
-        self.line_end = Some(line_end);
-    }
-
-    pub fn build(self) -> Option<Section> {
-        match (self.file_name, self.line_start, self.line_end) {
-            (Some(file_name), Some(line_start), Some(line_end)) => Some(Section {
-                file_name,
-                line_start,
-                line_end,
-            }),
-            _ => None,
-        }
-    }
+    pub line_start: u32,
+    pub line_end: u32,
 }
 
-impl Parser {
-    pub fn new() -> Self {
-        Self { verbose: false }
-    }
-
-    pub fn set_verbose(&mut self, verbose: bool) -> &mut Self {
-        self.verbose = verbose;
-        self
-    }
-
-    pub fn get_sections(&self, target_branch: &str) -> Result<Vec<Section>, crate::error::Error> {
-        self.diff(target_branch).map(|diff| self.sections(&diff))
-    }
-
-    fn diff(&self, target: &str) -> Result<String, crate::error::Error> {
-        let cmd_output = Command::new("git")
-            .args(&["diff", "-u", target])
-            .output()
-            .expect("Could not run git command.");
-        if self.verbose {
-            println!("{}", String::from_utf8(cmd_output.stdout.clone())?);
-        }
-        if cmd_output.status.success() {
-            Ok(String::from_utf8(cmd_output.stdout)?)
-        } else {
-            Err(String::from_utf8(cmd_output.stderr)?.into())
-        }
-    }
-
-    fn sections(&self, git_diff: &str) -> Vec<Section> {
-        let mut sections = Vec::new();
-        let mut file_name = "";
-        let mut line_start = 0;
-        let mut line_end = 0;
-        let mut current_line = 0;
-
-        for l in git_diff.lines() {
-            // File added or edited
-            // +++ b/Cargo.lock
-            if l.starts_with("+++") {
-                // Only add name of .rs file
-                if l.ends_with(".rs") {
-                    // TODO: do something less ugly with the bounds and indexing
-                    file_name = l[l.find('/').unwrap() + 1..].into();
-                }
-            }
-
-            // Actual diff lines
-            // '@@ -33,6 +33,9 @@ version = "0.1.0"'
-            if l.starts_with("@@") {
-                current_line = get_diff_line_start(&l) - 1;
-            }
-
-            // Increase the current line counter if added or untouched line in diff
-            if l.starts_with(' ') || (l.starts_with('+') && !l.starts_with("+++")) {
-                current_line += 1;
-            }
-
-            // Set the sections start & end if added line (+)
-            if l.starts_with('+') && !l.starts_with("+++") {
-                if line_start == 0 {
-                    line_start = current_line;
-                    line_end = line_start;
-                } else {
-                    line_end += 1;
-                }
-            // When consecutive added (+) lines stops, create the section and push it
-            } else if !l.starts_with('-') {
-                if line_start != 0 && file_name != "" {
-                    if let Some(s) = create_section(&file_name, line_start, line_end) {
-                        sections.push(s);
+pub fn get_sections<P>(repo_path: P, branch: &str) -> Result<Vec<Section>, Error>
+where
+    P: AsRef<Path>,
+{
+    let repo = Repository::discover(repo_path)?;
+    let tree = repo.revparse_single(branch)?.peel_to_tree()?;
+    let mut config = DiffOptions::default();
+    config.context_lines(0);
+
+    let diff = repo.diff_tree_to_workdir_with_index(Some(&tree), Some(&mut config))?;
+    let mut sections = Vec::new();
+
+    diff.foreach(
+        &mut |_delta, _progress| true,
+        None,
+        Some(&mut |delta, hunk| {
+            match delta.status() {
+                Delta::Modified | Delta::Added => {
+                    if let Some(file_path) = delta.new_file().path() {
+                        let file_path = file_path.to_string_lossy().to_string();
+
+                        if file_path.ends_with(".rs") {
+                            sections.push(Section {
+                                file_name: file_path,
+                                line_start: hunk.new_start(),
+                                line_end: hunk.new_start() + hunk.new_lines(),
+                            });
+                        }
                     }
                 }
-                // Resets start and end for next section
-                line_start = 0;
-                line_end = 0;
+                _ => {}
             }
-        }
-        if line_start != 0 && file_name != "" {
-            if let Some(s) = create_section(&file_name, line_start, line_end) {
-                sections.push(s);
-            }
-        }
-        sections
-    }
-}
+            true
+        }),
+        None,
+    )?;
 
-fn create_section(file_name: &str, line_start: i32, line_end: i32) -> Option<Section> {
-    let mut current_section = SectionBuilder::new();
-    current_section.file_name(file_name.to_string());
-    current_section.line_start(line_start);
-    current_section.line_end(line_end);
-    current_section.build()
-}
-
-fn get_diff_line_start(line: &str) -> i32 {
-    // @@ and space
-    let after_ats = &line[3..];
-    // space and @@
-    let before_second_ats_index = &after_ats.find("@@").unwrap() - 1;
-    // -33,6 +33,9
-    let diff_lines = &after_ats[..before_second_ats_index];
-    let (_, to) = diff_lines.split_at(diff_lines.find(' ').unwrap());
-    let added = to.trim();
-    let (added_start, _) = if let Some(index) = added[1..].find(',') {
-        let (a, b) = added[1..].split_at(index);
-        (a, &b[1..])
-    } else {
-        (added, "")
-    };
-    added_start.parse::<i32>().unwrap()
+    Ok(sections)
 }
 
 #[cfg(test)]
 mod tests {
-    #[test]
-    fn test_set_verbose() {
-        use crate::git::Parser;
+    use super::*;
+    use std::fs::{self, File};
+    use std::io::Write;
+    use tempfile::TempDir;
 
-        let mut parser = Parser::new();
-        assert_eq!(false, parser.verbose);
+    type Result<T> = std::result::Result<T, Error>;
 
-        let p2 = parser.set_verbose(true);
-        assert_eq!(true, p2.verbose);
-
-        let p3 = p2.set_verbose(false);
-        assert_eq!(false, p3.verbose);
-    }
     #[test]
-    fn test_empty_diff() {
-        use crate::git::{Parser, Section};
-        // Setup
-        let diff = r#""#;
-        let expected_sections: Vec<Section> = vec![];
-        let parser = Parser::new();
-        // Run
-        let actual_sections = parser.sections(diff);
-        // Assert
-        assert_eq!(expected_sections, actual_sections);
+    fn no_changes() -> Result<()> {
+        let repo = RepoFixture::new()?;
+
+        let sections = get_sections(repo.path(), "master").unwrap();
+        assert!(sections.is_empty());
+        Ok(())
     }
+
     #[test]
-    fn test_simple_diff() {
-        use crate::git::{Parser, Section};
-        // Setup
-        let diff = std::fs::read_to_string("test_files/git/one_diff.patch").unwrap();
-        let expected_sections: Vec<Section> = vec![
+    fn added_files() -> Result<()> {
+        let repo = RepoFixture::new()?
+            .write("foo.rs", "test_files/git/added/foo.rs")?
+            .write("bar.rs", "test_files/git/added/bar.rs")?
+            .stage(&["foo.rs", "bar.rs"])?;
+
+        let expected = vec![
             Section {
-                file_name: "src/git.rs".to_string(),
-                line_start: 7,
-                line_end: 7,
+                file_name: "bar.rs".into(),
+                line_start: 1,
+                line_end: 5,
             },
             Section {
-                file_name: "src/git.rs".to_string(),
-                line_start: 120,
-                line_end: 146,
+                file_name: "foo.rs".into(),
+                line_start: 1,
+                line_end: 7,
             },
         ];
-        let parser = Parser::new();
-        // Run
-        let actual_sections = parser.sections(&diff);
-        // Assert
-        assert_eq!(expected_sections, actual_sections);
+        let actual = get_sections(repo.path(), "master").unwrap();
+        assert_eq!(expected, actual);
+        Ok(())
     }
+
     #[test]
-    fn test_diff_several_files() {
-        use crate::git::{Parser, Section};
-        // Setup
-        let diff = std::fs::read_to_string("test_files/git/diff_several_files.patch").unwrap();
-        let expected_sections: Vec<Section> = vec![
+    fn modified_files() -> Result<()> {
+        let files = &["foo.rs", "bar.rs"];
+        let repo = RepoFixture::new()?
+            .write("foo.rs", "test_files/git/modified/old/foo.rs")?
+            .write("bar.rs", "test_files/git/modified/old/bar.rs")?
+            .stage(files)?
+            .commit("master", files)?
+            .write("foo.rs", "test_files/git/modified/new/foo.rs")?
+            .write("bar.rs", "test_files/git/modified/new/bar.rs")?;
+
+        let expected = vec![
             Section {
-                file_name: "src/clippy.rs".to_string(),
-                line_start: 127,
-                line_end: 128,
+                file_name: "bar.rs".into(),
+                line_start: 1,
+                line_end: 2,
             },
             Section {
-                file_name: "src/git.rs".to_string(),
-                line_start: 7,
-                line_end: 7,
+                file_name: "bar.rs".into(),
+                line_start: 5,
+                line_end: 9,
+            },
+            Section {
+                file_name: "foo.rs".into(),
+                line_start: 3,
+                line_end: 4,
             },
             Section {
-                file_name: "src/git.rs".to_string(),
-                line_start: 120,
-                line_end: 180,
+                file_name: "foo.rs".into(),
+                line_start: 6,
+                line_end: 7,
             },
         ];
-        let parser = Parser::new();
-        // Run
-        let actual_sections = parser.sections(&diff);
-        // Assert
-        assert_eq!(expected_sections, actual_sections);
+        let actual = get_sections(repo.path(), "master").unwrap();
+        assert_eq!(expected, actual);
+        Ok(())
+    }
+
+    #[test]
+    fn mixed_extensions() -> Result<()> {
+        let repo = RepoFixture::new()?
+            .write("foo.rs", "test_files/git/mixed/foo.rs")?
+            .write("bar.txt", "test_files/git/mixed/bar.txt")?
+            .stage(&["foo.rs", "bar.txt"])?;
+
+        let expected = vec![Section {
+            file_name: "foo.rs".into(),
+            line_start: 1,
+            line_end: 7,
+        }];
+        let actual = get_sections(repo.path(), "master").unwrap();
+        assert_eq!(expected, actual);
+        Ok(())
     }
+
     #[test]
-    fn test_diff_several_extensions_files() {
-        use crate::git::{Parser, Section};
-        // Setup
-        let diff = std::fs::read_to_string("test_files/git/diff_several_extensions_files.patch").unwrap();
-        let expected_sections: Vec<Section> = vec![
+    fn other_branch() -> Result<()> {
+        let repo = RepoFixture::new()?
+            .branch("other")?
+            .write("foo.rs", "test_files/git/modified/old/foo.rs")?
+            .stage(&["foo.rs"])?
+            .commit("other", &["foo.rs"])?
+            .write("foo.rs", "test_files/git/modified/new/foo.rs")?;
+
+        let expected = vec![
             Section {
-                file_name: "src/git.rs".to_string(),
-                line_start: 91,
-                line_end: 97,
+                file_name: "foo.rs".into(),
+                line_start: 3,
+                line_end: 4,
+            },
+            Section {
+                file_name: "foo.rs".into(),
+                line_start: 6,
+                line_end: 7,
             },
         ];
-        let parser = Parser::new();
-        // Run
-        let actual_sections = parser.sections(&diff);
-        // Assert
-        assert_eq!(expected_sections, actual_sections);
+        let actual = get_sections(repo.path(), "other").unwrap();
+        assert_eq!(expected, actual);
+        Ok(())
+    }
+
+    struct RepoFixture {
+        dir: TempDir,
+        repo: Repository,
+    }
+
+    impl RepoFixture {
+        pub fn new() -> Result<Self> {
+            let dir = TempDir::new()?;
+            let repo = Repository::init(dir.path())?;
+            {
+                // Set mandatory configuration
+                let mut config = repo.config()?;
+                config.set_str("user.name", "name")?;
+                config.set_str("user.email", "email")?;
+
+                // Write initial commit
+                let id = repo.index()?.write_tree()?;
+                let tree = repo.find_tree(id)?;
+                let sig = repo.signature()?;
+                repo.commit(Some("HEAD"), &sig, &sig, "initial", &tree, &[])?;
+            }
+            Ok(RepoFixture { dir, repo })
+        }
+
+        pub fn write(self, path: &str, test_file: &str) -> Result<Self> {
+            let contents = fs::read_to_string(test_file)?;
+            let mut file = File::create(self.dir.path().join(path))?;
+            file.write_all(contents.as_bytes())?;
+            Ok(self)
+        }
+
+        pub fn stage(self, paths: &[&str]) -> Result<Self> {
+            let mut index = self.repo.index()?;
+            for path in paths {
+                index.add_path(path.as_ref())?;
+            }
+            index.write()?;
+            Ok(self)
+        }
+
+        pub fn commit(self, branch: &str, paths: &[&str]) -> Result<Self> {
+            {
+                let mut index = self.repo.index()?;
+                for path in paths {
+                    index.add_path(path.as_ref())?;
+                }
+
+                let id = index.write_tree()?;
+                let tree = self.repo.find_tree(id)?;
+                let sig = self.repo.signature()?;
+
+                let target = self.repo.head()?.target().unwrap();
+                let parent = self.repo.find_commit(target)?;
+
+                let name = format!("refs/heads/{}", branch);
+                self.repo
+                    .commit(Some(&name), &sig, &sig, "some commit", &tree, &[&parent])?;
+            }
+            Ok(self)
+        }
+
+        pub fn branch(self, name: &str) -> Result<Self> {
+            {
+                let target = self.repo.head()?.target().unwrap();
+                let parent = self.repo.find_commit(target)?;
+
+                self.repo.branch(name, &parent, false)?;
+            }
+            Ok(self)
+        }
+
+        pub fn path(&self) -> &Path {
+            self.dir.path()
+        }
     }
 }
diff --git a/src/intersections.rs b/src/intersections.rs
index 3762a41..e5fa115 100644
--- a/src/intersections.rs
+++ b/src/intersections.rs
@@ -46,7 +46,7 @@ mod intersections_tests {
     use crate::git::Section;
     use crate::intersections::files_match;
 
-    type TestSection = (&'static str, i32, i32);
+    type TestSection = (&'static str, u32, u32);
     #[test]
 
     fn test_files_match() {
@@ -167,7 +167,7 @@ mod intersections_tests {
         }
     }
 
-    fn in_range(lint: (&str, i32, i32), section: (&str, i32, i32)) -> bool {
+    fn in_range(lint: (&str, u32, u32), section: (&str, u32, u32)) -> bool {
         use crate::intersections::lines_in_range;
         let clippy_lint = Span {
             file_name: String::from(lint.0),
diff --git a/src/main.rs b/src/main.rs
index 6e5753c..b2601c8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,4 @@
+use std::env;
 use structopt::StructOpt;
 
 mod cargo;
@@ -54,9 +55,7 @@ fn main() -> Result<(), error::Error> {
     let opts = Options::from_args();
 
     println!("Getting diff against target {}", opts.branch);
-    let diff_sections = git::Parser::new()
-        .set_verbose(opts.verbose)
-        .get_sections(&opts.branch)?;
+    let diff_sections = git::get_sections(env::current_dir()?, &opts.branch)?;
     println!("Checking Cargo manifest");
     let manifest = cargo::Parser::from_manifest_path(opts.cargo_toml)?;
     if manifest.is_workspace() {


Test patch:
diff --git a/test_files/git/added/bar.rs b/test_files/git/added/bar.rs
new file mode 100644
index 0000000..ed0c141
--- /dev/null
+++ b/test_files/git/added/bar.rs
@@ -0,0 +1,4 @@
+#[derive(Debug)]
+pub struct Test {
+    pub field: Option<usize>,
+}
diff --git a/test_files/git/added/foo.rs b/test_files/git/added/foo.rs
new file mode 100644
index 0000000..3135849
--- /dev/null
+++ b/test_files/git/added/foo.rs
@@ -0,0 +1,6 @@
+#[derive(Debug, PartialEq)]
+pub struct Section {
+    pub file_name: String,
+    pub line_start: u32,
+    pub line_end: u32,
+}
diff --git a/test_files/git/diff_several_extensions_files.patch b/test_files/git/diff_several_extensions_files.patch
deleted file mode 100644
index d87f3da..0000000
--- a/test_files/git/diff_several_extensions_files.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-diff --git a/.travis.yml b/.travis.yml
-index 5ab5d03..40c43d0 100644
---- a/.travis.yml
-+++ b/.travis.yml
-@@ -12,3 +12,5 @@ before_script:
- script:
-   - cargo clippy -- -D clippy::pedantic
-   - cargo test --all
-+
-+# for test the only add diff of .rs files
-diff --git a/src/git.rs b/src/git.rs
-index 5e6c31e..1816c80 100644
---- a/src/git.rs
-+++ b/src/git.rs
-@@ -91,8 +91,13 @@ impl Parser {
-// File added or edited
-// +++ b/Cargo.lock
-if l.starts_with("+++") {
--// TODO: do something less ugly with the bounds and indexing
--file_name = l[l.find('/').unwrap() + 1..].into();
-+// Only add .rs file to the diff
-+if l.ends_with(".rs") {
-+    // TODO: do something less ugly with the bounds and indexing
-+    file_name = l[l.find('/').unwrap() + 1..].into();
-+} else {
-+    continue;
-+}
-}
diff --git a/test_files/git/diff_several_files.patch b/test_files/git/diff_several_files.patch
deleted file mode 100644
index 1541c09..0000000
--- a/test_files/git/diff_several_files.patch
+++ /dev/null
@@ -1,88 +0,0 @@
-diff --git a/src/clippy.rs b/src/clippy.rs
-index f394397..e6e05e9 100644
---- a/src/clippy.rs
-+++ b/src/clippy.rs
-@@ -124,3 +124,5 @@ mod tests {
-         assert_eq!(expected_lints, lints(clippy_output));
-     }
- }
-+
-+// this is a test
-diff --git a/src/git.rs b/src/git.rs
-index c5efb81..5926057 100644
---- a/src/git.rs
-+++ b/src/git.rs
-@@ -4,7 +4,7 @@ pub struct Parser {
-     verbose: bool,
- }
- 
--#[derive(Debug)]
-+#[derive(Debug, PartialEq)]
- pub struct Section {
-     pub file_name: String,
-     pub line_start: i32,
-@@ -117,3 +117,64 @@ impl Parser {
-         sections
-     }
- }
-+
-+mod tests {
-+    use crate::git::{Parser, Section};
-+    #[test]
-+    fn test_empty_diff() {
-+        // Setup
-+        let diff = r#""#;
-+        let expected_sections: Vec<Section> = vec![];
-+        let parser = Parser::new(false);
-+        // Run
-+        let actual_sections = parser.sections(diff);
-+        // Assert
-+        assert_eq!(expected_sections, actual_sections);
-+    }
-+
-+    #[test]
-+    fn test_simple_diff() {
-+        // Setup
-+        let diff = std::fs::read_to_string("test_files/git/one_diff.patch").unwrap();
-+        let expected_sections: Vec<Section> = vec![
-+            Section {
-+                file_name: "src/git.rs".to_string(),
-+                line_start: 4,
-+                line_end: 11,
-+            },
-+            Section {
-+                file_name: "src/git.rs".to_string(),
-+                line_start: 117,
-+                line_end: 147,
-+            },
-+        ];
-+        let parser = Parser::new(false);
-+        // Run
-+        let actual_sections = parser.sections(&diff);
-+        // Assert
-+        assert_eq!(expected_sections, actual_sections);
-+    }
-+
-+    #[test]
-+    fn test_diff_several_files() {
-+        // Setup
-+        let diff = std::fs::read_to_string("test_files/git/one_diff.patch").unwrap();
-+        let expected_sections: Vec<Section> = vec![
-+            Section {
-+                file_name: "src/git.rs".to_string(),
-+                line_start: 4,
-+                line_end: 11,
-+            },
-+            Section {
-+                file_name: "src/git.rs".to_string(),
-+                line_start: 117,
-+                line_end: 147,
-+            },
-+        ];
-+        let parser = Parser::new(false);
-+        // Run
-+        let actual_sections = parser.sections(&diff);
-+        // Assert
-+        assert_eq!(expected_sections, actual_sections);
-+    }
-+}
diff --git a/test_files/git/mixed/bar.txt b/test_files/git/mixed/bar.txt
new file mode 100644
index 0000000..d39e2c7
--- /dev/null
+++ b/test_files/git/mixed/bar.txt
@@ -0,0 +1,2 @@
+I'm not a rust file!
+
diff --git a/test_files/git/mixed/foo.rs b/test_files/git/mixed/foo.rs
new file mode 100644
index 0000000..3135849
--- /dev/null
+++ b/test_files/git/mixed/foo.rs
@@ -0,0 +1,6 @@
+#[derive(Debug, PartialEq)]
+pub struct Section {
+    pub file_name: String,
+    pub line_start: u32,
+    pub line_end: u32,
+}
diff --git a/test_files/git/modified/new/bar.rs b/test_files/git/modified/new/bar.rs
new file mode 100644
index 0000000..67e7249
--- /dev/null
+++ b/test_files/git/modified/new/bar.rs
@@ -0,0 +1,8 @@
+#[derive(Debug, PartialEq)]
+pub struct Test {
+    pub field: Option<usize>,
+}
+
+pub struct Another {
+    pub another_field: PathBuf,
+}
diff --git a/test_files/git/modified/new/foo.rs b/test_files/git/modified/new/foo.rs
new file mode 100644
index 0000000..263637f
--- /dev/null
+++ b/test_files/git/modified/new/foo.rs
@@ -0,0 +1,7 @@
+#[derive(Debug, PartialEq)]
+pub struct Section {
+    pub file_names: String,
+    pub line_start: u32,
+    pub line_end: u32,
+    pub more_foo: Option<i32>,
+}
diff --git a/test_files/git/modified/old/bar.rs b/test_files/git/modified/old/bar.rs
new file mode 100644
index 0000000..ed0c141
--- /dev/null
+++ b/test_files/git/modified/old/bar.rs
@@ -0,0 +1,4 @@
+#[derive(Debug)]
+pub struct Test {
+    pub field: Option<usize>,
+}
diff --git a/test_files/git/modified/old/foo.rs b/test_files/git/modified/old/foo.rs
new file mode 100644
index 0000000..3135849
--- /dev/null
+++ b/test_files/git/modified/old/foo.rs
@@ -0,0 +1,6 @@
+#[derive(Debug, PartialEq)]
+pub struct Section {
+    pub file_name: String,
+    pub line_start: u32,
+    pub line_end: u32,
+}
diff --git a/test_files/git/one_diff.patch b/test_files/git/one_diff.patch
deleted file mode 100644
index fa1e739..0000000
--- a/test_files/git/one_diff.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-diff --git a/src/git.rs b/src/git.rs
-index c5efb81..764864c 100644
---- a/src/git.rs
-+++ b/src/git.rs
-@@ -4,7 +4,7 @@ pub struct Parser {
-     verbose: bool,
- }
- 
--#[derive(Debug)]
-+#[derive(Debug, PartialEq)]
- pub struct Section {
-     pub file_name: String,
-     pub line_start: i32,
-@@ -117,3 +117,30 @@ impl Parser {
-         sections
-     }
- }
-+
-+mod tests {
-+    use crate::git::{Parser, Section};
-+    #[test]
-+    fn test_empty_diff() {
-+        // Setup
-+        let diff = r#""#;
-+        let expected_sections: Vec<Section> = vec![];
-+        let parser = Parser::new(false);
-+        // Run
-+        let actual_sections = parser.sections(diff);
-+        // Assert
-+        assert_eq!(expected_sections, actual_sections);
-+    }
-+
-+    #[test]
-+    fn test_one_diff() {
-+        // Setup
-+        let diff = r#""#;
-+        let expected_sections: Vec<Section> = vec![];
-+        let parser = Parser::new(false);
-+        // Run
-+        let actual_sections = parser.sections(diff);
-+        // Assert
-+        assert_eq!(expected_sections, actual_sections);
-+    }
-+}