using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
namespace LibGit2Sharp
{
///
/// Holds the result of a diff between two trees.
/// Changes at the granularity of the file can be obtained through the different sub-collections , and .
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class TreeChanges : IEnumerable
{
private readonly IDictionary changes = new Dictionary();
private readonly List added = new List();
private readonly List deleted = new List();
private readonly List modified = new List();
private readonly List typeChanged = new List();
private readonly List unmodified = new List();
private int linesAdded;
private int linesDeleted;
private readonly IDictionary> fileDispatcher = Build();
private readonly StringBuilder fullPatchBuilder = new StringBuilder();
private static IDictionary> Build()
{
return new Dictionary>
{
{ ChangeKind.Modified, (de, d) => de.modified.Add(d) },
{ ChangeKind.Deleted, (de, d) => de.deleted.Add(d) },
{ ChangeKind.Added, (de, d) => de.added.Add(d) },
{ ChangeKind.TypeChanged, (de, d) => de.typeChanged.Add(d) },
{ ChangeKind.Unmodified, (de, d) => de.unmodified.Add(d) },
};
}
///
/// Needed for mocking purposes.
///
protected TreeChanges()
{ }
internal TreeChanges(DiffListSafeHandle diff)
{
Proxy.git_diff_foreach(diff, FileCallback, null, DataCallback);
Proxy.git_diff_print_patch(diff, PrintCallBack);
}
private int DataCallback(GitDiffDelta delta, GitDiffRange range, GitDiffLineOrigin lineOrigin, IntPtr content, UIntPtr contentLen, IntPtr payload)
{
var filePath = FilePathMarshaler.FromNative(delta.NewFile.Path);
AddLineChange(this[filePath], lineOrigin);
return 0;
}
private int FileCallback(GitDiffDelta delta, float progress, IntPtr payload)
{
AddFileChange(delta);
return 0;
}
private int PrintCallBack(GitDiffDelta delta, GitDiffRange range, GitDiffLineOrigin lineorigin, IntPtr content, UIntPtr contentlen, IntPtr payload)
{
string formattedoutput = Utf8Marshaler.FromNative(content, (int)contentlen);
var filePath = FilePathMarshaler.FromNative(delta.NewFile.Path);
fullPatchBuilder.Append(formattedoutput);
this[filePath].AppendToPatch(formattedoutput);
return 0;
}
private void AddLineChange(Changes currentChange, GitDiffLineOrigin lineOrigin)
{
switch (lineOrigin)
{
case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION:
linesAdded++;
currentChange.LinesAdded++;
break;
case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION:
linesDeleted++;
currentChange.LinesDeleted++;
break;
}
}
private void AddFileChange(GitDiffDelta delta)
{
var newFilePath = FilePathMarshaler.FromNative(delta.NewFile.Path);
var oldFilePath = FilePathMarshaler.FromNative(delta.OldFile.Path);
var newMode = (Mode)delta.NewFile.Mode;
var oldMode = (Mode)delta.OldFile.Mode;
var newOid = delta.NewFile.Oid;
var oldOid = delta.OldFile.Oid;
if (delta.Status == ChangeKind.Untracked || delta.Status == ChangeKind.Ignored)
{
delta.Status = ChangeKind.Added;
}
var diffFile = new TreeEntryChanges(newFilePath, newMode, newOid, delta.Status, oldFilePath, oldMode, oldOid, delta.IsBinary());
fileDispatcher[delta.Status](this, diffFile);
changes.Add(newFilePath, diffFile);
}
#region IEnumerable Members
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
public virtual IEnumerator GetEnumerator()
{
return changes.Values.GetEnumerator();
}
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
///
/// Gets the corresponding to the specified .
///
public virtual TreeEntryChanges this[string path]
{
get { return this[(FilePath)path]; }
}
private TreeEntryChanges this[FilePath path]
{
get
{
TreeEntryChanges treeEntryChanges;
if (changes.TryGetValue(path, out treeEntryChanges))
{
return treeEntryChanges;
}
return null;
}
}
///
/// List of that have been been added.
///
public virtual IEnumerable Added
{
get { return added; }
}
///
/// List of that have been deleted.
///
public virtual IEnumerable Deleted
{
get { return deleted; }
}
///
/// List of that have been modified.
///
public virtual IEnumerable Modified
{
get { return modified; }
}
///
/// List of which type have been changed.
///
public virtual IEnumerable TypeChanged
{
get { return typeChanged; }
}
///
/// The total number of lines added in this diff.
///
public virtual int LinesAdded
{
get { return linesAdded; }
}
///
/// The total number of lines added in this diff.
///
public virtual int LinesDeleted
{
get { return linesDeleted; }
}
///
/// The full patch file of this diff.
///
public virtual string Patch
{
get { return fullPatchBuilder.ToString(); }
}
private string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"+{0} ~{2} -{1} \u00B1{3}",
Added.Count(), Deleted.Count(),
Modified.Count(), TypeChanged.Count());
}
}
}
}