Skip to content

Commit 8f45678

Browse files
author
Sebastian Hönel
committed
Merge branch 'develop'
2 parents f17491a + ec0db45 commit 8f45678

28 files changed

+483
-111
lines changed

GitDensity/Density/Hunk.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ private Hunk ComputeLinesAddedAndDeleted()
137137
public Boolean RepresentsNewEmptyFile =>
138138
this.OldLineStart == 0u && this.OldNumberOfLines == 0u && this.NewLineStart == 0u && this.NewNumberOfLines == 0u && this.Patch == String.Empty;
139139

140+
/// <summary>
141+
/// An override for <see cref="HunksForPatch(PatchEntryChanges, DirectoryInfo, DirectoryInfo)"/>
142+
/// that uses a dummy directory. Use this method only when you do not have any
143+
/// reason to access or rely on a valid <see cref="Hunk.SourceFilePath"/> or
144+
/// <see cref="Hunk.TargetFilePath"/> (useful for purely virtual use-cases when
145+
/// no access to the underlying file is needed).
146+
/// </summary>
147+
/// <param name="pec"></param>
148+
/// <returns></returns>
149+
public static IEnumerable<Hunk> HunksForPatch(PatchEntryChanges pec)
150+
{
151+
var dummyDir = new DirectoryInfo(Path.GetTempPath());
152+
return Hunk.HunksForPatch(pec, dummyDir, dummyDir);
153+
}
154+
140155
/// <summary>
141156
/// Returns an <see cref="IEnumerable{Hunk}"/> containing all hunks
142157
/// for the given <see cref="PatchEntryChanges"/>.
@@ -150,13 +165,15 @@ public static IEnumerable<Hunk> HunksForPatch(PatchEntryChanges pec, DirectoryIn
150165
String fullSourcePath, fullTargetPath;
151166
Exception hunkPathException;
152167

153-
// First condition is an empty patch that is usually the result from adding a new, empty file.
154-
// We will only return one empty Hunk for this case.
155-
// Second condition is a pure Move/Rename (then there's no real diff).
156-
// Third condition is a deletion of a whole file.
168+
// First condition is an empty patch that is usually the result from adding a new, empty, diffable file.
169+
// Second condition is a pure Move/Rename (then there's no real diff, i.e. no added/del'd lines).
170+
// Third condition is a deletion of a whole, empty, diffable file.
171+
//
172+
// We will only return one empty Hunk for this case. This is important as all methods expect
173+
// at least one Hunk from this enumeration, even if it's empty (check references).
157174
if ((pec.Mode == Mode.NonExecutableFile && pec.OldMode == Mode.Nonexistent && pec.LinesAdded == 0)
158175
|| (pec.Status == ChangeKind.Renamed && pec.LinesAdded == 0 && pec.LinesDeleted == 0)
159-
|| (pec.Status == ChangeKind.Deleted && pec.Mode == Mode.Nonexistent))
176+
|| (pec.Status == ChangeKind.Deleted && pec.Mode == Mode.Nonexistent && pec.LinesDeleted == 0))
160177
{
161178
if (!Hunk.TryGetHunkPaths(
162179
pairSourceDirectory, pairTargetDirectory,
@@ -215,7 +232,7 @@ public static IEnumerable<Hunk> HunksForPatch(PatchEntryChanges pec, DirectoryIn
215232
/// directories and filenames. However, sometimes these contain invalid chars. In that
216233
/// case, this method will resort to manually concatenating these and return false; true,
217234
/// otherwise.
218-
/// A warning is logged using the <see cref="Hunk"/>'s logger.
235+
/// A warning is logged using the <see cref="Hunk"/>'s <see cref="logger"/>.
219236
/// </summary>
220237
/// <param name="pairSourceDirectory"></param>
221238
/// <param name="pairTargetDirectory"></param>
@@ -255,6 +272,15 @@ protected static Boolean TryGetHunkPaths(
255272
}
256273
}
257274

275+
/// <summary>
276+
/// Issues a warning using the <see cref="logger"/>, when, for some reason,
277+
/// the method <see cref="TryGetHunkPaths(DirectoryInfo, DirectoryInfo, string, string, out string, out string, out Exception)"/> fails to obtain the <see cref="Hunk"/>'s paths.
278+
/// </summary>
279+
/// <param name="ex"></param>
280+
/// <param name="pairSourceDirectory"></param>
281+
/// <param name="pairTargetDirectory"></param>
282+
/// <param name="hunkSourcePath"></param>
283+
/// <param name="hunkTargetPath"></param>
258284
protected static void WarnAboutBrokenHunkPaths(
259285
Exception ex,
260286
DirectoryInfo pairSourceDirectory, DirectoryInfo pairTargetDirectory,

GitDensity/GitDensity.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<Import Project="..\packages\LibGit2Sharp.NativeBinaries.1.0.210\build\net461\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\packages\LibGit2Sharp.NativeBinaries.1.0.210\build\net461\LibGit2Sharp.NativeBinaries.props')" />
3+
<Import Project="..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props')" />
44
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
55
<PropertyGroup>
66
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -56,8 +56,8 @@
5656
<Reference Include="Iesi.Collections, Version=4.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
5757
<HintPath>..\packages\Iesi.Collections.4.0.4\lib\net461\Iesi.Collections.dll</HintPath>
5858
</Reference>
59-
<Reference Include="LibGit2Sharp, Version=0.25.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
60-
<HintPath>..\packages\LibGit2Sharp.0.25.0\lib\netstandard2.0\LibGit2Sharp.dll</HintPath>
59+
<Reference Include="LibGit2Sharp, Version=0.26.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
60+
<HintPath>..\packages\LibGit2Sharp.0.26.0\lib\net46\LibGit2Sharp.dll</HintPath>
6161
</Reference>
6262
<Reference Include="Microsoft.Extensions.Configuration, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
6363
<HintPath>..\packages\Microsoft.Extensions.Configuration.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll</HintPath>
@@ -210,9 +210,9 @@
210210
<PropertyGroup>
211211
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
212212
</PropertyGroup>
213-
<Error Condition="!Exists('..\packages\LibGit2Sharp.NativeBinaries.1.0.210\build\net461\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.NativeBinaries.1.0.210\build\net461\LibGit2Sharp.NativeBinaries.props'))" />
214213
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets'))" />
215214
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.109.1\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.109.1\build\net46\System.Data.SQLite.Core.targets'))" />
215+
<Error Condition="!Exists('..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props'))" />
216216
</Target>
217217
<Import Project="..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" />
218218
<Import Project="..\packages\System.Data.SQLite.Core.1.0.109.1\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.109.1\build\net46\System.Data.SQLite.Core.targets')" />

GitDensity/Similarity/TextBlock.cs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
/// ---------------------------------------------------------------------------------
1515
///
1616
using GitDensity.Density;
17+
using LibGit2Sharp;
1718
using System;
1819
using System.Collections.Generic;
1920
using System.Collections.ObjectModel;
@@ -38,7 +39,7 @@ public enum TextBlockType
3839
/// Represents a block of text where the block consists of lines and their
3940
/// line numbers.
4041
/// </summary>
41-
internal class TextBlock : IEquatable<TextBlock>, ICloneable
42+
public class TextBlock : IEquatable<TextBlock>, ICloneable
4243
{
4344
protected IDictionary<UInt32, Line> linesWithLineNumber;
4445

@@ -351,5 +352,71 @@ public object Clone()
351352
tb.AddLines(this.LinesWithNumber.Select(kv => kv.Value.Clone() as Line));
352353
return tb;
353354
}
355+
356+
/// <summary>
357+
/// Using <see cref="TextBlock"/>s, this method counts the net- and gross-amount
358+
/// of lines added and deleted across all <see cref="Hunk"/>s of a <see cref="PatchEntryChanges"/> entity.
359+
/// Uses <see cref="RemoveEmptyLinesAndComments()"/> to count the net-affected lines.
360+
/// </summary>
361+
/// <param name="pec"></param>
362+
/// <param name="linesAddedGross">Amount of lines added across all hunks.</param>
363+
/// <param name="linesDeletedGross">Amount of lines deleted across all hunks.</param>
364+
/// <param name="linesAddedWithoutEmptyOrComments">Amount of lines added net across
365+
/// all hunks. This count is equal to or lower than linesAddedGross, as empty lines
366+
/// and comments are removed.</param>
367+
/// <param name="linesDeletedWithoutEmptyOrComments">Amount of lines deleted net
368+
/// across all hunks. This count is equal to or lower than linesDeletedGross, as empty
369+
/// lines and comments are not considered in the deleted lines.</param>
370+
public static void CountLinesInPatch(PatchEntryChanges pec,
371+
out UInt32 linesAddedGross,
372+
out UInt32 linesDeletedGross,
373+
out UInt32 linesAddedWithoutEmptyOrComments,
374+
out UInt32 linesDeletedWithoutEmptyOrComments)
375+
{
376+
var hunks = Hunk.HunksForPatch(pec);
377+
378+
var tbsAdded = hunks.Select(hunk => new TextBlock(hunk, TextBlockType.New)).ToList();
379+
var tbsDeleted = hunks.Select(hunk => new TextBlock(hunk, TextBlockType.Old)).ToList();
380+
381+
linesAddedGross = (UInt32)tbsAdded.Sum(tb => tb.LinesAdded);
382+
linesDeletedGross = (UInt32)tbsDeleted.Sum(tb => tb.LinesDeleted);
383+
384+
linesAddedWithoutEmptyOrComments = (UInt32)tbsAdded
385+
.Select(tb => tb.RemoveEmptyLinesAndComments())
386+
.Sum(tb => tb.LinesAdded);
387+
linesDeletedWithoutEmptyOrComments = (UInt32)tbsDeleted
388+
.Select(tb => tb.RemoveEmptyLinesAndComments())
389+
.Sum(tb => tb.LinesDeleted);
390+
}
391+
392+
/// <summary>
393+
/// Uses <see cref="CountLinesInPatch(PatchEntryChanges, out uint, out uint, out uint, out uint)"/> to aggregate the line-counts for a list of <see cref="PatchEntryChanges"/>.
394+
/// This is useful if multiple changes are to be considered. This is the case for
395+
/// e.g. counting all lines across all modified/renamed files. Please see the documentation
396+
/// for the referenced method.
397+
/// </summary>
398+
/// <param name="pecs"></param>
399+
/// <param name="linesAddedGross"></param>
400+
/// <param name="linesDeletedGross"></param>
401+
/// <param name="linesAddedWithoutEmptyOrComments"></param>
402+
/// <param name="linesDeletedWithoutEmptyOrComments"></param>
403+
public static void CountLinesInAllPatches(IEnumerable<PatchEntryChanges> pecs,
404+
out UInt32 linesAddedGross,
405+
out UInt32 linesDeletedGross,
406+
out UInt32 linesAddedWithoutEmptyOrComments,
407+
out UInt32 linesDeletedWithoutEmptyOrComments)
408+
{
409+
linesAddedGross = linesDeletedGross = linesAddedWithoutEmptyOrComments = linesDeletedWithoutEmptyOrComments = 0u;
410+
411+
foreach (var pec in pecs)
412+
{
413+
TextBlock.CountLinesInPatch(
414+
pec, out uint add, out uint del, out uint addNoC, out uint delNoC);
415+
linesAddedGross += add;
416+
linesDeletedGross += del;
417+
linesAddedWithoutEmptyOrComments += addNoC;
418+
linesDeletedWithoutEmptyOrComments += delNoC;
419+
}
420+
}
354421
}
355422
}

GitDensity/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<package id="F23.StringSimilarity" version="3.0.0" targetFramework="net461" />
77
<package id="FluentNHibernate" version="2.1.2" targetFramework="net461" />
88
<package id="Iesi.Collections" version="4.0.4" targetFramework="net461" />
9-
<package id="LibGit2Sharp" version="0.25.0" targetFramework="net461" />
10-
<package id="LibGit2Sharp.NativeBinaries" version="1.0.210" targetFramework="net461" />
9+
<package id="LibGit2Sharp" version="0.26.0" targetFramework="net461" />
10+
<package id="LibGit2Sharp.NativeBinaries" version="2.0.267" targetFramework="net461" />
1111
<package id="Microsoft.Extensions.Configuration" version="2.1.1" targetFramework="net461" />
1212
<package id="Microsoft.Extensions.Configuration.Abstractions" version="2.1.1" targetFramework="net461" />
1313
<package id="Microsoft.Extensions.Configuration.Binder" version="2.1.1" targetFramework="net461" />

GitDensityTests/GitDensityTests.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props')" />
34
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
45
<PropertyGroup>
56
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -39,6 +40,9 @@
3940
<WarningLevel>4</WarningLevel>
4041
</PropertyGroup>
4142
<ItemGroup>
43+
<Reference Include="LibGit2Sharp, Version=0.26.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
44+
<HintPath>..\packages\LibGit2Sharp.0.26.0\lib\net46\LibGit2Sharp.dll</HintPath>
45+
</Reference>
4246
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
4347
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
4448
</Reference>
@@ -75,6 +79,7 @@
7579
</PropertyGroup>
7680
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
7781
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
82+
<Error Condition="!Exists('..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.NativeBinaries.2.0.267\build\net46\LibGit2Sharp.NativeBinaries.props'))" />
7883
</Target>
7984
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
8085
</Project>

GitDensityTests/HunkTests.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,30 @@
1313
///
1414
/// ---------------------------------------------------------------------------------
1515
///
16+
using GitDensity.Density;
17+
using GitDensity.Similarity;
18+
using LibGit2Sharp;
1619
using Microsoft.VisualStudio.TestTools.UnitTesting;
1720
using System;
1821
using System.Collections.Generic;
22+
using System.IO;
1923
using System.Linq;
24+
using System.Reflection;
2025
using System.Text;
2126
using System.Text.RegularExpressions;
2227
using System.Threading.Tasks;
28+
using Util.Density;
29+
using Util.Extensions;
2330

2431
namespace GitDensityTests
2532
{
2633
[TestClass]
2734
public class HunkTests
2835
{
36+
public static DirectoryInfo SolutionDirectory
37+
=> new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Locati‌​on)).Parent.Parent.Parent;
38+
39+
2940
[TestMethod]
3041
public void TestPatchSplit()
3142
{
@@ -90,5 +101,71 @@ public void TestPatchSplitMultiLines()
90101
Assert.AreEqual("-deleted line\n" +
91102
"yo", hunks[1].Patch);
92103
}
104+
105+
[TestMethod]
106+
public void TestHunkGetLines()
107+
{
108+
using (var span = new Util.GitCommitSpan(SolutionDirectory.FullName.OpenRepository()))
109+
{
110+
// Has additions and deletions, also within comments
111+
var comm1 = span.Where(c => c.Sha.StartsWith("6f18a54", StringComparison.OrdinalIgnoreCase)).First();
112+
//// Has mostly additions but also (single-line) comments we wanna remove
113+
//var comm2 = span.Where(c => c.Sha.StartsWith("f7574a4", StringComparison.OrdinalIgnoreCase)).First();
114+
115+
var pair = CommitPair.FromChild(comm1, span.Repository);
116+
// Each change corresponds to a modified file:
117+
var changes = pair.RelevantTreeChanges.Where(rtc => rtc.Status == ChangeKind.Modified);
118+
119+
// Let's look at 'IMetricsAnalyzer':
120+
var patch = pair.Patch[changes.Where(c => c.Path.EndsWith("IMetricsAnalyzer.cs")).First().Path];
121+
122+
TextBlock.CountLinesInPatch(
123+
patch, out uint add, out uint del, out uint addNoC, out uint delNoC);
124+
125+
Assert.AreEqual(7u, add);
126+
Assert.AreEqual(3u, del);
127+
Assert.AreEqual(2u, addNoC);
128+
Assert.AreEqual(1u, delNoC);
129+
130+
131+
// Now assert that this commit does not have any pure adds/renames:
132+
changes = pair.RelevantTreeChanges.Where(rtc => rtc.Status == ChangeKind.Added || rtc.Status == ChangeKind.Renamed);
133+
TextBlock.CountLinesInAllPatches(
134+
changes.Select(c => pair.Patch[c.Path]), out add, out del, out addNoC, out delNoC);
135+
136+
Assert.AreEqual(0u, add);
137+
Assert.AreEqual(0u, del);
138+
Assert.AreEqual(0u, addNoC);
139+
Assert.AreEqual(0u, delNoC);
140+
141+
// .. also pure deletes:
142+
changes = pair.RelevantTreeChanges.Where(rtc => rtc.Status == ChangeKind.Deleted);
143+
Assert.IsTrue(!changes.Any());
144+
TextBlock.CountLinesInAllPatches(
145+
changes.Select(c => pair.Patch[c.OldPath]), out add, out del, out addNoC, out delNoC);
146+
147+
Assert.AreEqual(0u, add);
148+
Assert.AreEqual(0u, del);
149+
Assert.AreEqual(0u, addNoC);
150+
Assert.AreEqual(0u, delNoC);
151+
152+
153+
//////////////////////////// Let's check a deleted file:
154+
comm1 = span.Where(c => c.Sha.StartsWith("03e2779", StringComparison.OrdinalIgnoreCase)).First();
155+
pair = CommitPair.FromChild(comm1, span.Repository);
156+
changes = pair.RelevantTreeChanges.Where(rtc => rtc.Status == ChangeKind.Deleted);
157+
158+
Assert.AreEqual(3, changes.Count());
159+
160+
// Per file, only 2 lines are not comments!
161+
TextBlock.CountLinesInAllPatches(
162+
changes.Select(c => pair.Patch[c.Path]), out add, out del, out addNoC, out delNoC);
163+
164+
Assert.AreEqual(0u, add);
165+
Assert.IsTrue(del >= 90u); // didn't count it, but roughly 3x30
166+
Assert.AreEqual(0u, addNoC);
167+
Assert.AreEqual(6u, delNoC);
168+
}
169+
}
93170
}
94171
}

GitDensityTests/packages.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="LibGit2Sharp" version="0.26.0" targetFramework="net461" />
4+
<package id="LibGit2Sharp.NativeBinaries" version="2.0.267" targetFramework="net461" />
35
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net461" />
46
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net461" />
57
</packages>

0 commit comments

Comments
 (0)