I am writing a utility to collect git file additions and deletions. Git or rather Bitbucket reports commit changes in unified diff (unidiff) format which looks like this:

diff --git a/CodeQualityPortal/ViewModels/FileChurnSummary.cs b/CodeQualityPortal/ViewModels/FileChurnSummary.cs  
new file mode 100644  
index 0000000..5094e11  
--- /dev/null
+++ b/FileChurnSummary.cs
@@ -0,0 +1,9 @@
+using System;
-
+namespace CodeQualityPortal.ViewModels
+{
+    public class FileChurnSummary : FileCodeChurn
+    {
+        public DateTime Date { get; set; }
+    }
+}
\ No newline at end of file

This means that FileChurnSummary.cs has 8 additions and 1 deletion.

I was considering writing a full blown parser to deal with chunk hunks and ranges to return me the file name with changes, but straightforward regex approach turned out to be to be not too bad:

public class DiffParser  
{
    public ICollection<File> Parse (string input)
    {
        var files = new List<File>();

        var diffRegex = new Regex("diff --git");
        var fileNameRegex = new Regex("(?<=\\+\\+\\+ b/).*[^\\r\\n]");            
        var plusRegex = new Regex("^\\+", RegexOptions.Multiline); // Will match +++ as well
        var minusRegex = new Regex("^\\-", RegexOptions.Multiline);            
        var diffMatches  = diffRegex.Matches(input);
        for (var index = 0; index < diffMatches.Count; index++)
        {
            var diffMatch = diffMatches[index];
            var startIndex = diffMatch.Index;
            var nextMatch = diffMatch.NextMatch();
            var endIndex = nextMatch.Index != 0 ? nextMatch.Index : input.Length - 1;
            var diffText = input.Substring(startIndex, endIndex - startIndex + 1);

            var fileNameMatch = fileNameRegex.Match(diffText);
            var plusMatches = plusRegex.Matches(diffText);
            var minusMatches = minusRegex.Matches(diffText);
            files.Add(new File { FileName = fileNameMatch.Value, Additions = plusMatches.Count - 1, Deletions = minusMatches.Count - 1 });                
        }

        return files;
    }
}

Where File is

public class File  
{        
    public string FileName { get; set; }        
    public int Additions { get; set; }
    public int Deletions { get; set; }
    public int Changes { get; set; }
}